How does try/catch/finally work in EK9?
← Error Handling and Exceptions · Ref: Q135
The finally block in EK9 guarantees cleanup code runs regardless of whether the try block succeeds, an exception is caught, or a guard is unset.
FULL PATTERN
The complete try/catch/finally structure:
try riskyOperation() catch -> ex as Exception handleError(ex) finally cleanup()
The finally block always runs after try and catch complete.
FINALLY ALWAYS RUNS
Finally executes in all three scenarios:
1. Try completes normally (no exception)
2. Exception thrown and caught
3. Guard variable is unset (try body skipped)
This guarantee makes finally ideal for cleanup operations.
TRY/FINALLY WITHOUT CATCH
You can use try/finally without a catch block:
try doWork() finally cleanup()
If an exception occurs, it propagates after finally runs.
CLEANUP PATTERNS
Common uses for finally:
- Logging completion status
- Resetting state
- Releasing resources (though try-with-resources is preferred)
NESTED TRY BLOCKS
Try blocks can be nested. Each has its own catch and finally:
try try innerOperation() catch -> ex as Exception handleInner(ex) finally innerCleanup() finally outerCleanup()
See Q134 for basic try/catch. See Q137 for try-with-resources (automatic cleanup). See Q78 for guard variables in try blocks.
Example
defines module qa.errorhandling.trycatchfinally defines function riskyOperation() -> shouldFail as Boolean <- rtn <- String() if shouldFail ex <- Exception("Operation failed") throw ex rtn: "success" defines program TryCatchFinallyDemo() stdout <- Stdout() // === FULL PATTERN: try/catch/finally === stdout.println("=== Normal execution ===") try result <- riskyOperation(false) stdout.println("Try: " + result) catch -> ex as Exception stdout.println("Catch: " + $ex) finally stdout.println("Finally: always runs") // === EXCEPTION PATH === stdout.println("=== Exception path ===") try result <- riskyOperation(true) stdout.println("Try: " + result) catch -> ex as Exception stdout.println("Catch: " + ex.reason()) finally stdout.println("Finally: still runs after catch") // === TRY/FINALLY WITHOUT CATCH === stdout.println("=== Try/finally without catch ===") try stdout.println("Try: doing work") finally stdout.println("Finally: cleanup without catch") // === NESTED TRY BLOCKS === stdout.println("=== Nested try blocks ===") try try result <- riskyOperation(true) stdout.println("Inner try: " + result) catch -> ex as Exception stdout.println("Inner catch: " + ex.reason()) finally stdout.println("Inner finally") finally stdout.println("Outer finally")
Common mistakes
E50001 — Catch blocks accept only a single exception parameter. EK9 does not support multi-catch syntax like Java. E50001 — only a single Exception is supported. Use the Exception base type and dispatch inside the catch body. See ek9 -h E50001 for details.
Incorrect:
catch -> ex1 as Exception ex2 as Exception
Correct:
catch -> ex as Exception
E04030 — The catch parameter must be an Exception type or subtype. Using Integer triggers E04030 — type must be of Exception type. See ek9 -h E04030 for details.
Incorrect:
-> ex as Integer stdout.println("Catch: " + $ex)
Correct:
-> ex as Exception stdout.println("Catch: " + $ex)
E50060 — EK9 Exception has no getMessage() method. Use reason() to get the exception message. See ek9 -h E50060 for details.
Incorrect:
stdout.println("Catch: " + ex.getMessage())
Correct:
stdout.println("Catch: " + ex.reason())
E04030 — Only Exception types can be thrown in EK9. Throwing a String triggers E04030 — type must be of Exception type. Wrap the message in an Exception constructor. See ek9 -h E04030 for details.
Incorrect:
ex <- "Operation failed" throw ex
Correct:
ex <- Exception("Operation failed") throw ex
E50060 — EK9 Exception has no getMessage() method. Use reason() for the exception message. See ek9 -h E50060 for details.
Incorrect:
stdout.println("Inner catch: " + ex.getMessage())
Correct:
stdout.println("Inner catch: " + ex.reason())
E50060 — EK9 has no toString() method. Use string concatenation or interpolation for string conversion. See ek9 -h E50060 for details.
Incorrect:
stdout.println(result.toString())
Correct:
stdout.println("Try: " + result)
Other ways to ask this
- What does finally do in EK9?
- How do I guarantee cleanup in EK9?
- Does EK9 have try-finally?
Coming from another language?
Java: try { } catch { } finally { } with identical guarantee. Python: try: ... except: ... finally: ... same semantics. Rust: no try/finally, uses RAII (Drop trait) for cleanup. Go: defer statement provides cleanup guarantee. Kotlin: try/catch/finally like Java. Swift: defer keyword for cleanup (runs when scope exits, same purpose as finally), no try/finally syntax. EK9: try/catch/finally with same guarantee, also works with guard variables (unset guard skips try body but finally still runs).
Keywords: handle, pattern, defer, swift, nested, resource, finally, always, try, guarantee, exception, error, catch, cleanup