Manuscript Documentation
v0.1.0

Programming faults#

A programming fault means an assumption the code relies on has failed. Use ordinary conditional returns to check that assumption. For ordinary invalid user input, use conditions and return a Result error so the caller can respond.

Invariant checks#

Use ordinary if and return control flow to check an invariant. Import panic from @std/faults.ms when a violated invariant must produce a programming fault.

Manuscript
import panic from "@std/faults.ms"

fn discount_validated(price: Decimal, percent: Decimal) -> Decimal:
    if price <= 0d:
        return panic(message: "Price must be positive")
    if percent <= 0d or percent >= 100d:
        return panic(message: "Percent must be positive and below 100")
    return price * (1d - percent / 100d)

This example expects strictly positive prices and percentages below 100 as preconditions. A violation is treated as a programming fault. Use the validating discount function in expected failures when these values come from ordinary runtime input.

Explicit faults#

An explicit panic(message: message) produces a programming fault when code determines that an invariant has failed.

Its source type is fn(message: String) -> Never. It never completes normally; a call can appear where any expression result type is expected, including an Int return position. This does not create a catchable expected-error value or permit normal fallthrough in a non-Unit function. See Unit, Never, and return completeness .

Manuscript
panic(message: "Required invariant was violated.")

A fault aborts the current operation after cleanup. Programming faults are distinct from expected Result errors.

Runaway recursion#

Source calls may nest 200,000 deep. A call past that limit is a programming fault, call depth limit exceeded (200000 nested calls), with the source frames of the innermost calls and a note that outer frames are omitted. It unwinds, runs cleanup, and fails a test like any other fault, so the rest of a test run continues. Any fault trace keeps only its innermost 32 frames. Comparing values with == or != recurses through nested records, alternatives, and collections; past 250,000 nested values the comparison is the fault equality depth limit exceeded (250000 nested values), so a very long linked structure cannot crash the process.

Cleanup and observation#

Cleanup still runs after a fault. A cleanup fault can replace a pending return, domain error, or cancellation, while secondary faults are retained as diagnostics. The full selection and cleanup rules are in lexical cleanup .

A TaskGroup or failed Task join observes an operation fault as ExecutionError.Crashed after siblings are cancelled and joined. Scheduler faults go to the service’s failure observer. A caller’s own fault still unwinds it; try only propagates Result errors. See execution errors .