User Guide¶
kotlin-throwable-policy is for containment boundaries: places that catch broadly so one unit of
work does not terminate a process, but where abort signals and fatal errors still must not be buried.
Propagation Boundary¶
import one.wabbit.throwables.Throwables
fun handleRequest(block: () -> Unit): Result<Unit> =
try {
block()
Result.success(Unit)
} catch (t: Throwable) {
Throwables.propagateIfNeeded(t)
Result.failure(t)
}
propagateIfNeeded delegates to mustPropagate. When it finds a throwable that the policy says
must propagate, it rethrows that throwable.
Propagation precedence is:
- fatal platform errors
- interruption or cancellation
- non-fatal errors
- non-cancellation control flow
- contract violations
Classification¶
Use classification helpers when you need a decision without throwing:
val verdict = Throwables.isCancellation(t)
if (verdict.isYes) {
// Treat as cooperative cancellation, not an application error.
}
Verdict.UNKNOWN means traversal reached the configured depth limit before the category could be
ruled out.
isControlFlow treats interruption and cancellation as higher-priority abort signals. If either is
found, it returns Verdict.NO for control flow.
Policy Modes¶
The default policy is conservative about allocation. It follows the preferred cause chain and common wrapper exceptions, but it does not scan suppressed exceptions.
Use a custom or strict policy when correctness against complex throwable graphs matters more than allocation avoidance:
val policy = Throwables.defaultPolicy.copy(
allowAllocations = true,
scanSuppressed = true,
)
scanSuppressed = true requires allowAllocations = true.
The rethrowControlFlow, rethrowCancellations, rethrowInterrupts, and
rethrowContractViolations flags decide which non-fatal categories mustPropagate returns. Fatal
platform errors always propagate regardless of those flags.
Suppressed Failures¶
Use handleSuppressed when cleanup fails while another failure is already primary:
try {
cleanup()
} catch (suppressed: Throwable) {
Throwables.handleSuppressed(primary, suppressed)
}
If the cleanup failure is a higher-priority signal, it is thrown and the primary failure is attached as suppressed on a best-effort basis.
When restoreInterrupt is enabled, handleSuppressed restores the interrupt flag if the cleanup
failure contains an interruption signal.