5. Errors
When something goes wrong at run time — an index past the end of a list, a
int("nope"), a key that isn't in a map — Korrin raises an exception. Left
alone, it stops the program and prints a diagnostic. You can catch it instead.
try / except
try:
n = int("not a number")
print(n)
except err:
print("could not parse: {err.message}")
# => could not parse: `int` cannot parse "not a number" as a number
The body runs; if it raises, the except block runs with the error bound to the
name you give (err here). A caught built-in fault is an Error with two
fields:
try:
print([1, 2, 3][10])
except e:
print(e.code)
print(e.message)
# => E0307
# => index 10 is out of range for a list of length 3
If you don't need the value, leave the name out:
try:
risky = 1 / 0
except:
print("handled")
# => handled
raise
Raise your own error with raise. Error(message) is the usual thing to raise,
but any value works:
fn withdraw(balance, amount):
if amount > balance:
raise Error("insufficient funds")
return balance - amount
try:
withdraw(100, 250)
except e:
print(e.message)
# => insufficient funds
There is one except per try — it catches everything. To handle only some
cases, check inside and re-raise the rest:
try:
try:
raise Error("timeout")
except e:
if e.message == "timeout":
print("retrying")
else:
raise e
except outer:
print("gave up: {outer.message}")
# => retrying
finally
A finally block runs no matter how the try exits — normal completion, a
caught error, an uncaught one passing through, even a return:
fn read_config():
try:
return "config contents"
finally:
print("closing the file")
print(read_config())
# => closing the file
# => config contents
Because finally always runs, a return (or raise, or break) inside it
wins over whatever the try was going to do — including swallowing an error.
Use it for cleanup, not for control flow.
Uncaught errors
An exception with no try around it ends the program:
$ korrin run bank.kor
[E0312] Error: uncaught error: insufficient funds
╭─[bank.kor:8:9]
│
8 │ raise Error("insufficient funds")
│ ─────────────┬────────────
│ ╰── raised here
───╯
Every built-in fault has a code (E0307, E0304, …); the
error reference lists them, and they are what err.code
gives you.