# Error handling

> Handling errors in Langoost with try, catch, throw, and defer, plus the runtime.error and runtime.panic helpers.

# Error handling

Langoost handles recoverable errors with `try` / `catch` / `throw`, and runs
cleanup with `defer`.

## try / catch

Wrap code that might fail in `try`, and handle the thrown value in `catch`. The
`catch` clause binds the thrown value to a variable. This includes errors raised
by standard-library functions (a failed `io`, `net`, or `sql` call, say) — they
propagate to the nearest `catch`, where `e.message` describes what went wrong:

```goost
try {
    let data = io.read("config.json")
    process(data)
} catch err {
    println("failed: " + toString(err))
}
```

## finally

An optional `finally` block runs on every exit path — normal completion, after
a `catch`, and even when the `catch` itself re-throws (the value still unwinds
*after* `finally` runs):

```goost
try {
    work()
} catch err {
    println("error: " + toString(err))
} finally {
    cleanup()          // always runs
}
```

Use `finally` for cleanup tied to a single block; use `defer` (below) for
cleanup tied to the whole function.

## throw

Raise an error with `throw`. You can throw any value; an object is a convenient
way to carry structured detail:

```goost
fn parsePort(s: string): int {
    let n = toInt(s)
    if n <= 0 {
        throw {kind: "ParseError", message: "invalid port: " + s}
    }
    return n
}
```

An uncaught `throw` propagates up the call stack; if it reaches the top it
aborts the program with the error.

## defer

A `defer` statement schedules a function call to run when the enclosing function
returns — whether it returns normally or unwinds from a `throw`. Deferred calls
run in LIFO (last-in, first-out) order, and their arguments are evaluated
immediately:

```goost
fn copyFile(src: string, dst: string) {
    let input = io.stream.open(src, "r")
    defer io.stream.close(input)       // always runs on return

    let output = io.stream.open(dst, "w")
    defer io.stream.close(output)

    io.stream.write(output, io.stream.readAll(input))
}
```

## runtime helpers

The `runtime` module complements these keywords:

- `runtime.error.new(msg)` builds a structured error string, with
  `runtime.error.message(e)` and `runtime.error.isError(v)` to inspect it.
- `runtime.panic.throw(msg)` prints to stderr and exits with code 2 — for
  unrecoverable conditions rather than expected failures.

See the **[`runtime` module](/docs/stdlib/runtime)** for details.