What is a Panic in EK9 and when does the language raise one?
← Error Handling and Exceptions · Ref: Q1434
A Panic is the language reporting a broken rule that the program's own logic should have prevented. Only the language raises one. User code cannot create, throw or catch a Panic, and it always ends the program with exit status 1.
WHEN THE LANGUAGE RAISES A PANIC
- A require condition is false or unset (see Q1435).
- A constrained type is given a value outside its constraint at run time. Percentage(150) below is one.
- A for-range loop is given an unset start or end.
WHY A catch DOES NOT SEE IT
Panic is not a subtype of Exception, and a catch can only name an Exception, so no catch matches a Panic. 'catch -> ex as Panic' is E04030. In the program below the catch never runs. The finally block still runs, so cleanup and teardown happen. Then the program prints one line on stderr, 'Exception in thread "main" Panic: Constraint violation for qa.errorhandling.whatispanic::Percentage: Exit Code: 1', and exits 1.
WHY YOU CANNOT CREATE OR THROW ONE
Panic has no constructors user code can call, so 'Panic(...)' - with any arguments, or none - is E50060, and so is 'throw Panic(...)'. Even if you had one, only an Exception can be thrown. To end a program with a failure status you choose, throw an Exception with an exit code and do not catch it (see Q1432). The exit code of a Panic is always 1.
CONVERTING UNTRUSTED VALUES WITHOUT A PANIC
Percentage().of(score) gives an UNSET Percentage instead of a Panic when the value is out of range, and you check it with '?'. Use the bare constructor only when an out-of-range value would be a bug.
IN TESTS
assertPanic in a @Test is the one way to observe a Panic (see Q1435).
See Q1431 for every way a program sets its exit status. See Q304 for require.
Example
defines module qa.errorhandling.whatispanic defines type Percentage as Integer constrain as >= 0 and <= 100 defines function toPercentage() -> score as Integer <- rtn as Percentage: Percentage(score) defines program PanicDemo() stdout <- Stdout() stdout.println(`In range: ${toPercentage(Integer("42"))}`) try stdout.println("Converting 150") stdout.println($toPercentage(Integer("150"))) catch -> ex as Exception stdout.println(`Caught: ${ex}`) finally stdout.println("finally still runs")
Common mistakes
E04030 — A catch can only name an Exception or a subtype of it. Panic is not an Exception, so it cannot be caught. Prevent the Panic instead, for example with Percentage().of(score) and a '?' check. See ek9 -h E04030 for details.
Incorrect:
-> ex as Panic
Correct:
-> ex as Exception
Other ways to ask this
- Why can't my catch block catch a Panic?
- Can I throw a Panic myself?
- What exit code does a Panic give?
- What does 'Exception in thread main Panic: Constraint violation' mean?
Coming from another language?
Go: panic() can be called by user code and recover() can stop it. Rust: panic!() is user-callable, and catch_unwind can catch it in some cases. Java: Error subclasses can still be caught. EK9: Panic is raised only by the language, no catch can see it, finally blocks still run, and the program exits 1.
Keywords: require, catch, fatal, panic, exit code 1, uncatchable, constraint violation