Tasks: When a task fails
Also known as: unhandled promise rejection unobserved task exception fire and forget swallowed error lost error background task failed
● Advanced4 min
A task's error comes out of .wait(), like the error of an ordinary call, and an error nobody reads is printed, not lost.
fn parse_age(text: String) -> Int ! ParseError {
try Int(text)
}
fn main() {
const good = spawn parse_age("31")
const bad = spawn parse_age("thirty")
print(good.wait() catch _ => -1)
print(bad.wait() catch e => {
print("the task failed: {e}")
-1
})
}The program ran and printed:
kavach run wait-error.kav
31the task failed: `thirty` is not a valid Int-1
.wait() gives the task's result or its error; try passes it on, catch handles it. Calling .wait() again gives the same.
An error nobody reads
A task that failed and is let go of before any .wait() read its error doesn't lose it: the error goes to standard error, with where the task was spawned. Then the program goes on, and its exit code doesn't change:
use std.time
fn save(report: String) -> Int ! Error {
fail Error("disk full")
}
fn main() uses Console, Clock {
// `_`: nobody waits for this task, on purpose
const _saving = spawn save("weekly report")
time.sleep(10.ms) // the task runs and fails meanwhile
print("main goes on")
}main goes on error in a task nobody waited for: disk full (spawned at src/main.kav:9)
- A task is let go of when the function that spawned it returns. A
Taskhanded out to other code is let go of when its last holder lets it go; in native code, at the latest when the program ends. - An error that a
.wait()read, as in the first example, is not printed again. - An agent's event that runs again after a restart says so instead, with the event's number:
error in an event run again after a restart: …. See the audit log.
A spawn on a line of its own starts a task nobody waits for, as go f() does in Go. spawn save(r) is the same as const _t = spawn save(r):
fn save(report: String) -> Int ! Error {
fail Error("disk full")
}
fn greet(name: String) uses Console {
print("hello, {name}")
}
fn main() uses Console {
spawn greet("ann") // nobody waits for it
spawn save("weekly report") // nor for this one, and its error is printed
print("main goes on")
}main goes on hello, ann error in a task nobody waited for: disk full (spawned at src/main.kav:11)
- The tasks are still cancelled when
mainreturns. A task cancelled that way stops at its next wait; these two have none, so they finish first. - An error in such a task is still printed, with where it was spawned.
A Task kept in a name that is never used gets a warning. A name starting with _, as in _saving above, says nobody waits for it on purpose and silences the warning.
fn save(report: String) -> Int ! Error {
fail Error("disk full")
}
fn main() {
const saving = spawn save("weekly report")
}The compiler warned, then the program ran:
kavach run task-unused.kav
warning: `saving` is never used: remove it, or start its name with `_` if that is on purposeA panic is not an error, but a panic in a task stops only that task: it is printed when it happens, and .wait() fails with panicked: <message> at <file>:<line>. See Panics.