Repository navigation
Error Handling
Nodus provides structured error handling with try/catch/finally and throw. The exception model is stable as of v1.0.
try {
let data = read_file("config.json")
print(data)
} catch err {
print("Failed: " + err.message)
}
fn read_config(path) {
try {
let data = read_file(path)
return data
} catch err {
print("Error: " + err.message)
return nil
} finally {
print("read_config done")
}
}
The finally block always runs — on normal success, on caught exception, and on return from inside the try or catch block.
All five exit paths from a try/catch/finally are handled correctly:
| Path | Description | finally runs? |
|---|---|---|
| 1. Normal | try body completes without error, no catch | Yes |
| 2. Caught exception | exception in try, catch binds err and runs |
Yes |
| 3. Uncaught exception | exception in try, no matching handler, propagates | Yes |
| 4. return inside try |
return executes inside the try body |
Yes — before the function returns |
| 5. return inside catch |
return executes inside the catch block |
Yes — before the function returns |
fn example() {
try {
return "from try"
} catch err {
print("error")
} finally {
print("finally runs before the return completes")
}
}
print(example())
// Output:
// finally runs before the return completes
// from try
Inside a catch block, err is a record with these fields:
| Field | Type | Description |
|---|---|---|
err.message |
string | Human-readable error description (always present) |
err.kind |
string | Error category (see table below) |
err.payload |
any | Original thrown value when throw <non-string>; nil otherwise |
| Kind | When raised |
|---|---|
"name" |
Undefined variable accessed |
"type" |
Type mismatch (e.g. add number to nil) |
"index" |
List index out of range |
"key" |
Map key not found |
"thrown" |
throw <non-string value> |
"sandbox" |
Runtime limit exceeded (steps, time, output, stack depth) |
"import" |
Module resolution failure |
Throw a string — becomes err.message:
throw "file not found"
// err.message = "file not found"
// err.kind = "thrown"
// err.payload = nil
Throw a structured value — preserved as err.payload:
throw record { code: 404, reason: "not found" }
// err.kind = "thrown"
// err.payload.code = 404
// err.payload.reason = "not found"
// err.message = stringified representation
try {
throw record { code: 404, reason: "not found" }
} catch err {
print(err.kind) // "thrown"
print(err.payload.code) // 404
print(err.payload.reason) // "not found"
}
fn process(data) {
try {
try {
let result = parse(data)
return result
} catch inner_err {
print("parse error: " + inner_err.message)
throw inner_err.message // re-throw as string
}
} catch outer_err {
print("outer caught: " + outer_err.message)
return nil
} finally {
print("outer finally")
}
}
Each try block has its own handler. An exception propagates up through nested handlers until caught.
A finally clause requires a preceding catch clause. try { } finally { } without catch is a syntax error.
Nodus raises two categories of errors from its own internals:
-
LangSyntaxError— raised by the lexer or parser (syntax, import resolution, cyclic imports) -
LangRuntimeError— raised by the VM (name errors, type errors, index errors, etc.)
Both propagate into the catch block as the err record.
Uncaught exceptions print a compact stack trace:
Type error at script.nd:15:9: Cannot add number and nil
Stack trace:
at compute (script.nd:15:9)
called from main (script.nd:22:5)
fn safe_read(path) {
try {
return read_file(path)
} catch err {
return nil
}
}
fn load(path) {
try {
return read_file(path)
} catch err {
print("load failed: " + err.message)
throw err.message
}
}
fn with_resource(path) {
let resource = nil
try {
resource = acquire(path)
return process(resource)
} catch err {
print("error: " + err.message)
return nil
} finally {
if (resource != nil) {
release(resource)
}
}
}
See also Control Flow and Syntax Reference.
Nodus v5.15.0 — Release · GitHub · Issues · Changelog
Opcode set frozen at v1.0. See Bytecode Reference for the freeze declaration.
Getting Started
Language Reference
- Syntax Reference
- Type System
- Control Flow
- Functions
- Modules and Imports
- Error Handling
- Coroutines and Channels
- Workflows and Task Graphs
- Standard Library
Tooling
Embedding
Project