Language

Error handling

Functions signal problems by raising errors, and callers catch them with try/except blocks. When you want errors as values instead of exceptions, there is also Result[T, E] with Ok and Err variants.

Try / except / finally

def divide(a: Int, b: Int) -> Int {
    if b == 0 {
        raise ValueError("division by zero")
    }
    return a // b
}

try {
    let result = divide(10, 0)
} except ValueError as e {
    print("Bad value")
} except Error as e {
    print("Some other error")
} finally {
    print("Always runs")
}

except Error: is the broadest catch. Bare except: is not supported. You can raise a string directly: raise "message" wraps it in RuntimeError.

Error types

TypeUse case
ErrorBase of all Vary errors
RuntimeErrorGeneral runtime errors
TypeErrorType mismatches
ValueErrorInvalid values
IOErrorI/O failures
KeyErrorMissing dictionary keys
IndexErrorOut-of-bounds access
ContractViolationFailed precondition or postcondition
ProcessErrorSubprocess execution failure (not found, permission denied, timeout)
TimeoutErrorTask group exceeded its timeout

Result type

Result[T, E] represents success or failure without exceptions:

def divide(a: Int, b: Int) -> Result[Int, Str] {
    if b == 0 {
        return Err("division by zero")
    }
    return Ok(a // b)
}

let r = divide(10, 3)
assert r.is_ok()
assert r.unwrap() == 3
MethodReturnsDescription
.is_ok()BoolTrue if Ok
.is_err()BoolTrue if Err
.unwrap()TUnwrap Ok value (panics on Err)
.unwrap_err()EUnwrap Err value (panics on Ok)
.unwrap_or(default)TUnwrap Ok, or return default

Match on Result with case Ok(value) and case Err(error).

The ? operator

The ? postfix operator unwraps Ok or returns Err early:

def process() -> Result[None, FsError] {
    let wp = WritePath.of("/tmp/out.txt")?
    let _w = fs.write_text(wp, "hello")?
    return Ok(None)
}

The enclosing function must return a Result with the same error type.

The ?else return operator

When unwrapping a Result or optional, ?else return provides a default value and exits the function early if the unwrap fails:

def get_name(id: Int) -> Str {
    let user = find_user(id) ?else return "unknown"
    return user.name
}

This is useful when the enclosing function does not return a Result and you want a fallback instead of propagating the error.

← Collections and strings
Modules and imports →