Error handling: try / catch / throw / finally

try {
    throw "something went wrong"
} catch (ton.err) {
    print("caught: " + err)
}

Any runtime error — a native error (division by zero, wrong argument count, index out of bounds, ...) or an explicit throw — is catchable. The caught value is always the error's message as a string, bound to the name given in catch (name). break, continue, and return pass straight through an enclosing try untouched (they aren't errors).

finally adds a block that always runs after the try (and its catch, if any) — whether the block completed normally, threw, or hit a return:

ton.function readSafely() {
    try {
        return risky()
    } finally {
        print("cleanup ran")
    }
}

try needs at least one of catch/finally; catch alone (as above), finally alone (try { ... } finally { ... }, useful for cleanup when you want the error to keep propagating), or both together are all valid.

Throwing any value (since beta-2.0.0) — throw accepts any value, and catch receives exactly that value, so errors can carry structured data. Errors raised by the interpreter itself are still caught as a string ("Error at line 3: Division by zero.").

try {
    throw {code: 404, message: "not found"}
} catch (err) {
    print(err.code, err.message)      // 404 not found
}

ton.class ValidationError {
    ton.function init(field) { self.field = field }
    ton.function toString() { return "invalid field: " + self.field }
}
try { throw ValidationError("email") } catch e { print(e) }   // invalid field: email

Errors from native functions now include the line of the call (Error at line 7: sum() expects an array of numbers (found a string).). A return, break or continue inside finally takes precedence over the pending one.

Edit this page on GitHub