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.