Error Handling
BioLang uses try/catch for exception-style error handling and
assert(condition, "message") for precondition checks. Uncaught errors
propagate up the call stack automatically.
try / catch
Wrap fallible code in try { ... } catch e { ... }. The caught
value e is a string containing the error message:
# Catch a division-by-zero error
try {
let x = 1 / 0
} catch e {
println(f"Caught: {e}")
}
println("Execution continues after the catch block")
Error Types
Any runtime error can be caught: type errors, arithmetic errors, index-out-of-bounds, and I/O failures. The error message string describes the problem:
# Type error
try {
let x = "hello" + 42
} catch e {
println(f"Type error: {e}")
}
# Index out of bounds
try {
let xs = [1, 2, 3]
let bad = xs[10]
} catch e {
println(f"Index error: {e}")
}
try as an Expression
try/catch is an expression — it returns the value
of whichever branch executes:
# Provide a fallback value on failure
let result = try { 100 / 0 } catch _ { -1 }
println(f"Result: {result}")
# Fault-tolerant batch processing
let paths = ["good.csv", "missing.csv", "also_good.csv"]
let loaded = paths
|> map(|p| try { read_csv(p) } catch _ { nil })
|> filter(|x| x != nil)
assert(condition, message)
assert takes two arguments: a boolean condition and an error message
string. If the condition is false, the assertion throws an error that can be caught
with try/catch:
# Assertions that pass do nothing
assert(1 + 1 == 2, "math works")
assert(len([1, 2, 3]) == 3, "len is correct")
println("all assertions passed")
# A failing assertion throws an error
try {
assert(1 > 2, "one is not greater than two")
} catch e {
println(f"Caught assertion: {e}")
}
Error Propagation in Pipelines
Errors propagate automatically through pipe chains. If any step throws, the
entire chain stops and the error bubbles up to the nearest
try/catch:
# An error in any pipe stage stops the chain
fn must_be_positive(n) {
if n <= 0 {
error(f"expected positive, got {n}")
}
n
}
try {
let result = [-1, 2, 3]
|> map(must_be_positive)
|> sum()
println(f"Sum: {result}")
} catch e {
println(f"Pipeline failed: {e}")
}
Per-Item Error Handling
To skip failures rather than aborting the whole pipeline, catch errors inside
the map callback:
# Process each item, collect results and errors separately
let items = [10, 0, 5, 0, 20]
let results = items |> map(|x| {
try {
{value: 100 / x, ok: true}
} catch e {
{value: 0, ok: false}
}
})
let good = results |> filter(|r| r.ok) |> len()
let bad = results |> filter(|r| !r.ok) |> len()
println(f"Succeeded: {good}, Failed: {bad}")
Validation Pattern
# Use assert for precondition checks
fn analyze(scores) {
assert(len(scores) > 0, "scores list must not be empty")
let avg = sum(scores) / len(scores)
let mx = max(scores)
{average: avg, maximum: mx}
}
# Valid call
let r = analyze([85, 92, 78])
println(f"Average: {r.average}, Max: {r.maximum}")
# Invalid call — caught by try/catch
try {
analyze([])
} catch e {
println(f"Validation error: {e}")
}