Skip to content

Errors and Cleanup

Nubo uses catch name { ... } directly. There is no try keyword.

fn fail() void {
panic("Cannot load account")
}
catch err {
fail()
}
if !isNil(err) {
println(err.message)
println(string(err))
}

The block runs immediately. Its binding is an Error on failure and nil on success. panic accepts a message string; creating an Error does not throw it.

These types are global; no import or repeated declaration is needed.

TypeFields
SourceLocationfile: string, line: int, column: int, columnEnd: int
Errormessage: string, base: Error?, metaData: SourceLocation?, stackTrace: []SourceLocation
let location = SourceLocation("main.nubo", 10, 3, 12)
let cause = Error("Database unavailable")
let problem = Error("Cannot load account", base: cause, metaData: location)
problem.stackTrace.push(location)
println(typecheck(Error, problem))
println(string(problem))

Omitted location columns default to zero. Error construction supplies a fresh trace list, and copies a supplied list. Formatting includes source locations, frames, and causes, with circular-cause protection. Caught runtime errors populate available metadata automatically.

import io from "@std/io"
fn writeGreeting() void {
let file = io.open("greeting.txt", "w")
defer fn() void {
file.close()
}()
file.write("Hello, Nubo")
}
writeGreeting()

Deferred calls run in reverse registration order when their owning interpreter scope unwinds, including error paths. defer takes a call, not a function declaration alone.

A spawned worker’s failure is asynchronous: a catch around the launch is not a wait for that worker. Use explicit communication to report completion and results.