Exception Handling
Zeus provides exception handling with try, catch, and throw for robust error management.
The Error Class
Zeus has a built-in Error class that serves as the base for all exceptions:
// Create and throw an error with name and messagethrow new Error("ValidationError", "Invalid input value");The Error class has two properties:
name- The type/category of the error (e.g., “ValidationError”, “NetworkError”)message- A detailed description of what went wrong
Try-Catch Blocks
Handle runtime errors gracefully with try-catch:
function divide(a: i32, b: i32): i32 { if (b == 0) { throw new Error("DivisionError", "Division by zero"); } return a / b;}
try { let result: i32 = divide(10, 0); console.log(result);} catch (e: Error) { console.log("Caught " + e.name + ": " + e.message);}Throw Statement
Use throw to raise an exception:
function validateAge(age: i32) { if (age < 0) { throw new Error("ValidationError", "Age cannot be negative"); } if (age > 150) { throw new Error("ValidationError", "Age seems unrealistic"); }}Built-in Exceptions
Zeus automatically throws exceptions in certain error conditions:
NullReferenceException
Thrown when accessing a property on a null object:
class Person { public name: string; constructor(name: string) { this.name = name; }}
let person: Person; // null by defaultconsole.log(person.name); // Throws NullReferenceExceptionIndexOutOfBoundsException
Thrown when accessing an array with an invalid index:
let arr = [10, 20];
let value: i32 = arr[5]; // Throws IndexOutOfBoundsExceptionconsole.log(value);Stack Traces
When an exception is not caught, Zeus displays a detailed stack trace:
Unhandled Exception: Division by zero
Stack Trace: 0: divide at example.zs:3 2 | if (b == 0) { > 3 | throw new Error("Division by zero"); 4 | }
1: main at example.zs:10 9 | try { > 10 | let result: i32 = divide(10, 0); 11 | return result;Catching Exceptions
The catch block receives the exception object:
try { throw new Error("TestError", "Something went wrong");} catch (e: Error) { // Access the error name and message console.log("Caught " + e.name + ": " + e.message);}Custom Exception Types
Subclass Error to define your own exception types. Call super(name, message) from the
constructor to set the two Error properties, then throw and catch the subclass like any other type:
class NotFoundError extends Error { constructor(what: string) { super("NotFoundError", what + " was not found"); }}
try { throw new NotFoundError("user 42");} catch (e: NotFoundError) { console.log(e.name + ": " + e.message);}A catch (e: NotFoundError) matches only that type (and its subclasses); a catch (e: Error) still
catches everything, since every exception derives from Error.
Multiple Catch Clauses
A try can have several catch clauses. They are tried top-to-bottom and the first whose type
matches the thrown exception runs; the rest are skipped. Order specific subclasses before their base
class, since a catch (e: Error) matches everything.
class NetworkError extends Error { constructor() { super("NetworkError", "the connection dropped"); }}
try { throw new NetworkError();} catch (e: NetworkError) { console.log("network problem: " + e.message);} catch (e: Error) { console.log("some other error: " + e.message);}If no clause matches, the exception propagates to the next enclosing try (or terminates the program
as an unhandled exception).
The finally Block
A finally block runs on every exit path from the try/catch — normal completion, a caught
exception, an uncaught one on its way out, and a return, break, or continue that leaves the
block. Use it for cleanup that must always happen.
function run(): i32 { try { console.log("working"); return 1; } finally { console.log("cleanup always runs"); }}
console.log(run()); // prints: working / cleanup always runs / 1catch is optional when a finally is present — try { … } finally { … } is valid on its own. A
return inside the finally overrides a pending return from the try or catch:
function pick(): i32 { try { return 1; } finally { return 2; // wins — the function returns 2 }}
console.log(pick()); // 2Best Practices
Descriptive Error Names
Use meaningful error names to categorize errors:
// Good - clear error categorythrow new Error("ValidationError", "Invalid email format: missing @ symbol");throw new Error("NetworkError", "Connection timeout after 30 seconds");throw new Error("FileError", "Cannot read file: permission denied");
// Less helpful - generic namethrow new Error("Error", "Something went wrong");Early Validation
Validate inputs early and throw descriptive errors:
function processOrder(quantity: i32, price: f64): f64 { if (quantity <= 0) { throw new Error("ValidationError", "Quantity must be positive"); } if (price < 0.0) { throw new Error("ValidationError", "Price cannot be negative"); } return price * quantity;}Graceful Degradation
Handle errors gracefully when possible:
function safeGetElement(arr: i32[], index: i32): i32 { try { return arr[index]; } catch (e: Error) { console.log("Caught " + e.name + ", returning default value"); return 0; }}Coming Soon
| Feature | Status |
|---|---|
Explicit rethrow (throw;) | Planned |