Panic and defects
Understand which failures throw Panic, how to report them, and why they do not widen Result error types.
Panic represents an unrecoverable defect: user-supplied code broke a combinator contract or an asserted invariant failed. It is thrown, not returned as Err.
Why Panic exists
If .map() silently converted every thrown callback into Err<unknown>, Result<T, E> would stop describing expected failures accurately. Panic keeps domain errors typed and defects loud.
Common Panic sources
- callbacks passed to
map,mapError,andThen, recovery,match, or observation throw; - async callbacks reject;
Result.genbody or cleanup throws;- a generator returns a non-Result value;
unwrap()seesErr;- a retry predicate or dynamic delay throws;
- retry jitter is invalid;
- an input Promise to
allAsyncorpartitionAsyncrejects; - a codec schema throws or rejects instead of returning validation issues.
Detect and report
import { isPanic, Panic } from "better-result";
try {
runApplication();
} catch (error) {
if (isPanic(error)) {
reportDefect(error.message, error.cause);
throw error;
}
}
Equivalent guards:
Panic.is(value);
isPanic(value);
value instanceof Panic;
Panic has _tag: "Panic", message, cause, stack chaining, and toJSON().
Throw one deliberately
import { panic } from "better-result";
const unreachable = (value: never): never => panic("Unexpected application state", value);
Use this for impossible states and failed invariants, not ordinary validation.
Boundary guidance
Catch Panic only at a true defect boundary—process entry point, request crash reporter, worker supervisor, or test assertion. Log/report it with appropriate redaction, then usually let the current operation fail.