Reference
Errors & exit codes
Press Run to see a caught error; edit it (remove the try/recover) to see an uncaught Runtime error: ….
-- Doc example: how errors surface — raise, try/recover, and a clean message.
-- Edit this and press Run: remove the try/recover to see an uncaught
-- "Runtime error: x must be >= 0 …" instead.
intent: "doc example: errors"
task risky(x)
when x < 0
raise("x must be >= 0, got " + text(x))
give x * 2
task fails()
give risky(-1)
-- run shows the recovery in action:
try
print("risky(5) = " + text(risky(5)))
print("risky(-3) = " + text(risky(-3))) -- this one raises
recover err
print("caught → " + err)
test "raise fails; try/recover catches the message"
assert_eq(risky(5), 10)
let caught be ""
try
let bad be risky(-1)
recover err
set caught to err
assert(contains(caught, "must be >= 0"))
assert_error(fails)Raising & catching§
raise("message") -- fail deliberately (or re-propagate inside recover)
try
risky()
recover err
log "failed: " + err -- err is the message (text)
raise(err) -- RE-PROPAGATE; without it, recover swallows the error
give and stop are not errors — they pass through try/recover. (fail(...) builds an HTTP response, it does not raise.) Test that something raises with assert_error(task). The statement form raise "msg" / raise err (no parens) works too, and a bare raise alone is a parse error with the fix in the message. ⚠️ On the published v0.5.1 and older, raise "msg" without parens was two inert expressions — a silent no-op; on those binaries always raise("msg").
One more error worth recognizing on sight: 'decide' is a reserved word in Synsema; choose another name for the member after '.' (on v0.5.1 and older: the cryptic Expected IDENTIFIER, got DECIDE) means a hard keyword was used as an export, parameter, or member name — mod.decide(...), task wait(reason). The LLM words reason/decide/analyze/generate are reserved everywhere; rename (resolve, why).
Exit codes§
| Exit | When |
|---|---|
0 | success |
1 | parse error, runtime error, or any spawned agent ended in ERROR |
2 | usage error — a missing argument, synsema openapi without serve, or (v0.6.14+) an unknown --flag (rejected, not ignored, on run/test/conform) |
Plain run prints a stable one-liner: Runtime error: <file>:<line>:<col>: <msg>. Measuring it in a shell? Redirect (… >/dev/null; echo $?) — don't pipe before echo $?, or you read the pipe's code.
Rich diagnostics (--explain)§
synsema run --explain program.syn # human-readable, on stderr
synsema run --explain --format json program.syn # structured, for tools/agents
The report adds: source context, call stack, visible variables, the program intent, a classification (data / io / logic / capability / type), whether it's recoverable, and fix suggestions. The exit code is unchanged either way.
Common error shapes§
- Capability:
Capability not granted: net("…")— add the matchingrequire(or widen the scope). If it says declared but above the host ceiling, therequireis already there: only the host (--sandbox/--cap-set/the embedder's ceiling) can allow it.Capability not granted: file_read("…")from arendermeans a disk template read wasn't declared — addrequire file.read("…")(or bundle the template withsynsema build). - Pure profile:
<name>: not available in the pure profile — this run has no filesystem(and likewise no child processes / sockets / database drivers / scheduler threads) — the builtin is walled off by--profile pure; drop the flag to run it natively. See Sandboxing. run_program:above parent ceiling/above parent profilein the child's audit (a child asked for more than the parent lends — trimmed, not fatal);run_program: timed out after Ns(timed_out: true);run_program: max depth N exceeded;run_program: env "X" is a secret — reveal() it explicitly.synsema build:bundle corrupt (sha256 mismatch)(a tampered built binary refuses to run);"<path>" is part of the bundle (read-only)(a write to a bundled asset);this is a built program (synsema build); rebuild it…(--engine updateon a built binary); at build time,ausewith a dynamic path cannot be bundledand'…' escapes the bundle root.- Type: an operation got the wrong type (e.g.
as_secret(123)→ "expects text or bytes"). - Data: a missing map key, an out-of-range index, invalid JSON in
json_decode.