Menu

Exception Handling

Aussom has built-in exception handling with try, catch, and throw. Use it the same way you would in Java or JavaScript: wrap code that might fail in a try, then react to the failure in a catch. This page covers the common patterns.

try / catch

try {
    // code that might fail
}
catch (e) {
    // handle the failure
}

The catch block runs only if something inside the try block threw. The thrown value is bound to the catch variable - by convention named e.

class App {
    public main(args) {
        try {
            x = 10 / 0;
        } catch (e) {
            c.log("oops: " + e.getText());
        }
    }
}

Output:

oops: Divide by zero.

Exception inspection methods

The variable bound in catch is an exception object with these methods:

Method Returns
e.getText() The exception message text.
e.getDetails() The detail text. Often the same as getText().
e.getTrace() Just the stack trace, as a string.
e.getStackTrace() The message and the stack trace, as one string.

In application code, getText() is the one you usually want. When debugging, getStackTrace() is the most informative.

try {
    riskyOperation();
} catch (e) {
    c.warn("riskyOperation failed: " + e.getText());
    c.log(e.getStackTrace());
}

throw

Use throw to raise your own exception. The argument can be any expression that evaluates to a string.

public divide(int A, int B) {
    if (B == 0) {
        throw "divide: B must not be zero";
    }
    return A / B;
}

A thrown string is wrapped in an exception object before it reaches a catch, so the receiver still gets the standard getText(), getTrace(), and getStackTrace() methods.

When to catch and when to let it propagate

A few rules of thumb:

  • Catch where you can do something useful: log a friendly message, fall back to a default, retry, or skip the bad item in a loop.
  • Let the exception propagate when the function above you is in a better position to recover.
  • Avoid swallowing exceptions silently. If you have nothing useful to do, at least log the message so the cause is visible.
// Reasonable: skip a bad row, keep processing.
for (row : rows) {
    try {
        this.process(row);
    } catch (e) {
        c.warn("skipping row " + row.id + ": " + e.getText());
    }
}

// Reasonable: convert a low-level error to a domain-specific one.
public loadConfig(string Path) {
    try {
        return file.read(Path);
    } catch (e) {
        throw "Failed to load config '" + Path + "': " + e.getText();
    }
}

// Avoid: hides the cause and makes debugging painful.
try {
    riskyOperation();
} catch (e) {
    // ... nothing
}

Combining with the safe-access ? operator

For "the field might be missing" cases, the ? operator (see Operators) is usually a better fit than try/catch. It returns null for a missing member, missing map key, or out-of-range list index, which matches what you want most of the time.

Use try/catch for harder failures: divide by zero, IO errors, parse errors, network errors.

What to read next

  • Operators - the safe-access ? operator.
  • Modules - bringing in modules whose functions are commonly wrapped in try / catch (file, http, jdbc, and so on).