Error handling

try/catch/finally, throw, panic, and defer for recoverable and unrecoverable errors.

Summary

Any value can be thrown: strings, ints, maps, whatever makes sense. finally always runs, even when an exception is thrown. panic(msg) terminates immediately and is not catchable. defer schedules a block to run on function return, in LIFO order, even through exceptions. todo() and unreachable() both panic and are used as code-structure markers.

Canonical

Try / Catch / Finally

try {
    throw "something went wrong"
} catch e {
    println("Error: {e}")        -- Error: something went wrong
}

-- throw any value (string, int, map, whatever)
try {
    throw #{"kind": "NotFound", "msg": "missing"}
} catch e {
    println(e["msg"])            -- missing
}

-- finally always runs
try {
    throw "err"
} catch e {
    println("caught")
} finally {
    println("cleanup")           -- always executes
}

Nested Try/Catch

try {
    try {
        throw "inner"
    } catch e {
        throw "rethrown: {e}"
    }
} catch e {
    println(e)                   -- rethrown: inner
}

Throw from Functions

Exceptions propagate up the call stack:

fn divide(a, b) {
    if b == 0 { throw "division by zero" }
    return a / b
}

try {
    divide(10, 0)
} catch e {
    println(e)                   -- division by zero
}

Panic

panic(msg) terminates immediately. It is not catchable by try/catch.

panic("fatal: out of memory")
-- prints to stderr: xs: panic: fatal: out of memory
-- exits with code 1

Defer

defer schedules a block to run when the enclosing function returns. Multiple defers execute in LIFO (last-in, first-out) order.

fn example() {
    defer { println("third") }
    defer { println("second") }
    defer { println("first") }
    println("body")
}
example()
-- body
-- first
-- second
-- third

Defers run even if an exception is thrown.

When to Use What

MechanismCatchable?Use case
throw exprYesRecoverable errors: bad input, validation, missing data
panic(msg)NoUnrecoverable: invariant violations, impossible states
todo(msg?)NoPlaceholder for unimplemented code
unreachable()NoCode that should never execute