Docs
View as markdown

Exit codes and errors

The exit code of every relate CLI run at its own anchor, the error object a failed query run prints in the JSON modes, and how the two fit together.

cliagents

Every relate run ends with an exit code The number a relate run returns to the shell; each nonzero value names a class of failure, such as 2 for usage, 5 for conflict, 6 for a rejected statement. Read more in the glossary from one closed table, and each code has an anchor on this page, https://relatedb.ai/docs/reference/cli/exit-codes-and-errors#exit-<n>. A failed query run prints its message on stderr; in the json, jsonl, envelope and speedscope output modes it also prints one error object The JSON object a failed query run prints on stdout in the json, jsonl, envelope and speedscope modes: the class, the kind, the message, the exit code. Read more in the glossary on stdout, whose code names the class and whose exit_code equals the process exit code. The errors reference holds one row per error message.

Synopsis

A failed statement in --mode envelope, with the exit code echoed:

relate --mode envelope "MATCH (n RETURN n"
echo "exit=$?"

The first three lines are stderr, the fourth is stdout:

syntax error: parse error: expected RightParen, got "RETURN" (RETURN)
  MATCH (n RETURN n
           ^
{"error":{"code":"validation_error","column":10,"exit_code":6,"kind":"syntax","message":"syntax error: parse error: expected RightParen, got \"RETURN\" (RETURN)   MATCH (n RETURN n            ^","retryable":false,"source":"MATCH (n RETURN n"}}
exit=6
RunstderrstdoutExit code
Query run that succeedsDiagnostics only: warnings, notes, table-mode plansThe results0
Query run that fails, in json, jsonl, envelope or speedscopeThe messageAny results already printed, then one error object1, 5, 6, or 7
Query run that fails, in table, csv, markdown, cypher or toon (agent mode's piped default)The messageNothing extra1, 5, 6, or 7
Usage error, in any modeError: <message>. An unknown flag, an unexpected argument, a missing value, a --read-only value not attached, or an unknown --namespace adds the usage line and a --help hint; a value rejected after parsing (--mode, --timeout, --width, --max-rows, --param, --command with a positional query, RELATE_AGENT_MODE) prints the Error: line aloneNothing2
A command (clone, skill, info, …)Its own messagesIts own output; never an error object0, 1, or 2

A run of several statements stops at the first one that fails and exits with that statement's code.

Exit codes

Exit codeCodeMeaningError kindsRetry
0successSuccess, including an empty result. --help and --version also exit 0.——
1fatal_errorRuntime failure: store open or I/O error, capacity limit, query execution failure, internal error, failed final checkpoint.execution, capacity, resource, storage, semantic, io, internalNo: retryable is false.
2usage_errorUnknown flag, bad or missing value, extra positional.——
3not_foundReserved (shared with neo4j-cli); not produced.——
4auth_errorReserved (shared with neo4j-cli); not produced.——
5conflictWrite-write conflict, or the store is locked by another opener.conflict, lockedOnly kind conflict (retryable is true); kind locked is not.
6validation_errorThe statement is rejected: syntax, semantic, argument or type error, including a write on a --read-only database.syntax, semantic, argument, typeNo: retryable is false.
7timeoutA statement exceeded --timeout.executionNo: retryable is false.
8upstream_errorReserved (shared with neo4j-cli); not produced.——

The kinds and the retry advice come from the CLI entries of the errors reference; a query run's error object carries them as kind and retryable.

Exit codes 3, 4 and 8 are reserved and never produced.

relate agent-context prints the same table for the installed binary, without the reserved rows. Each entry in the Error kinds column links that kind's own anchor in the errors reference, such as #cli-validation-error-syntax for a syntax error and #cli-timeout for a timeout.

The error object

A query run (a one-shot statement or a piped script) that fails in --mode json, --mode jsonl, --mode envelope or --mode speedscope prints one compact JSON object on stdout, after any results already written, on one line. It carries no timings or planCache. Its keys are sorted alphabetically, as in every JSON mode.

{"error":{"code":"validation_error","exit_code":6,"kind":"syntax","message":"syntax error: …","retryable":false}}
FieldTypePresentMeaning
codestringalwaysThe class name from the exit code table: fatal_error, conflict, validation_error or timeout
exit_codenumberalwaysThe process exit code, equal to the table row of code
kindstringalwaysThe engine's error variant: syntax, semantic, argument, type, execution, conflict, capacity, resource, storage, locked, io or internal
messagestringalwaysThe text also printed on stderr, sanitized to one line
retryablebooleanalwaystrue for a write-write conflict and for a --concurrent-logical-workspace-limit refusal while other statements hold the workspace pool; a locked store is false until its other opener exits
statementnumbermulti-statement runsThe 1-based index of the failed statement
statementsnumberwith statementHow many statements the input held
committednumberwith statementHow many earlier statements are durable; 0 inside an open .begin transaction
columnnumbersyntax errors, when knownThe 1-based character column within source
sourcestringsyntax errors, when knownThe statement line the parser echoed
linenumbersyntax errors, when the parser gave a position but no source lineThe 1-based line within the statement

A position field is left out, never null or 0, when the position is unknown. New fields may be added; existing fields and code values are never renamed or given a new meaning.

The CLI error object carries no GQL status code. Where an engine error has one, its row in the errors reference names it, and the GQL status index lists every status at its own anchor (#gql-42n02). The Python exception classes and the HTTP, Bolt and MCP errors are on the same page.

How the exit code and the error object relate

code and exit_code always name the same row. kind says which engine error variant produced it, so one exit code covers several kinds:

kindcodeExit coderetryable
syntax, semantic, argument, typevalidation_error6false
execution, capacity, storage, io, internalfatal_error1false
semantic, for a store that fails to open on an invalid engine setting such as RELATE_PREVIEWfatal_error1false
resource, a --concurrent-logical-workspace-limit refusalfatal_error1true while other statements hold the shared pool; false when the statement alone exceeds the limit
execution, for a statement cancelled by --timeouttimeout7false
conflictconflict5true
lockedconflict5false

Exit codes 0 and 2 never come with an error object: 0 is success, and a usage error is caught before any statement runs. The message of each error, with its cause and remedy, is in the CLI errors and engine errors tables.

A failure in four output modes

The statement in this cell divides by zero, which the engine rejects as a type error:

Cypher
RETURN 1/0 AS x
Expected error
error: type error: / by zero

The terminal prints what relate prints for the same statement in table, envelope, jsonl and toon, with the exit code. The statement runs in the browser on the engine the page loads, on an empty database opened read-only, as relate --read-only opens one. The exit code and the error's class follow from the error kind through the classes in the errors reference. Its presets include a syntax error, a missing parameter, a refused write and a run of three statements that stops at the second.

The statement as relate 0.1.2 prints it in four of its output modes, rendered from this page's recorded result by the terminal's renderers, which match the relate 0.1.2 binary byte for byte on the recorded runs, timing values aside. The recording keeps no timings, so each one shows as <ms>; relate prints the milliseconds of its own run, and the live terminal prints the engine's. Exit code 6, as relate 0.1.2 returns it.

relate classes this failure as validation_error, kind type, retryable false (engine-division-by-zero).

--mode table prints one line on stderr:

relate --read-only --mode table 'RETURN 1/0 AS x'
type error: / by zero

--mode envelope prints one line on stderr, then one on stdout:

relate --read-only --mode envelope 'RETURN 1/0 AS x'
type error: / by zero
{"error":{"code":"validation_error","exit_code":6,"kind":"type","message":"type error: / by zero","retryable":false}}

--mode jsonl prints one line on stderr, then one on stdout:

relate --read-only --mode jsonl 'RETURN 1/0 AS x'
type error: / by zero
{"error":{"code":"validation_error","exit_code":6,"kind":"type","message":"type error: / by zero","retryable":false}}

--mode toon prints one line on stderr:

relate --read-only --mode toon 'RETURN 1/0 AS x'
type error: / by zero

Live: the statement runs in your browser on an empty read-only database, as relate --read-only opens one, and the pane shows what relate prints in the mode you pick, with the exit code and, under "Run as an agent harness", the row cap; the statement can be edited or swapped for a preset.

Recorded runs

Each run shows stderr and stdout as one stream, in the order relate wrote them, then the exit code. A path inside the run's directory prints as <dir>.

Exit 0: success

An empty result is a success too: the envelope keeps its columns and rows is [].

relate --mode envelope "RETURN 1 AS ok"
echo "exit=$?"
relate --mode envelope "MATCH (n:Nope) RETURN n.p AS p"
echo "exit=$?"
{"columnTypes":["INTEGER"],"columns":["ok"],"notifications":[],"planCache":"miss","rows":[{"ok":1}],"timings":{"elapsed_ms":0.34937500000000005,"execute_ms":0.201167,"lex_parse_ms":0.029709,"lower_ms":0.029207999999999998,"parse_ms":0.09624999999999999,"plan_ms":0.042583,"result_generation_ms":0.000292,"rewrite_ms":0.0065},"truncated":false}
exit=0
{"columnTypes":["ANY"],"columns":["p"],"notifications":[],"planCache":"miss","rows":[],"timings":{"elapsed_ms":0.352833,"execute_ms":0.205334,"lex_parse_ms":0.026416,"lower_ms":0.036291000000000004,"parse_ms":0.093209,"plan_ms":0.047166,"result_generation_ms":0.00025,"rewrite_ms":0.007292},"truncated":false}
exit=0

Exit 1: a store that does not exist

A --read-only open never creates a store A relate database: a directory, a single .rl file, or :memory:, opened with --database. Read more in the glossary, so a missing path fails the open with the error kind storage:

relate --database ./missing.rl --read-only --mode envelope "RETURN 1 AS ok"
echo "exit=$?"
Failed to open Relate: storage error: database not found: <dir>/missing.rl
{"error":{"code":"fatal_error","exit_code":1,"kind":"storage","message":"storage error: database not found: <dir>/missing.rl","retryable":false}}
exit=1

The stderr line carries the prefix Failed to open Relate:; the object's message does not.

Exit 2: a usage error

An unknown flag exits before the store opens, so even --mode envelope prints no error object:

relate --mode envelope --bogus "RETURN 1 AS ok"
echo "exit=$?"
Error: unknown flag: --bogus
Usage: relate [OPTIONS] [--database PATH] [QUERY]
Run 'relate --help' for more information.
exit=2

A command's usage error names that command's usage line and help. The flags page lists the parsing rules behind these errors.

Exit 5: a locked store

A second process opening a store another process holds for writing fails with the error kind locked. This run is recorded with a second relate query holding s.rl open:

Failed to open Relate: storage locked: <dir>/s.rl
{"error":{"code":"conflict","exit_code":5,"kind":"locked","message":"storage locked: <dir>/s.rl","retryable":false}}

retryable is false: the same open fails until the other process exits. A directory store can be opened with --read-only beside its writer; Share one store between two processes covers the options.

Exit 6: a rejected statement

The error kinds syntax, semantic, argument and type exit 6. A type error raised while the statement runs is one of them:

relate --mode envelope "RETURN 1/0 AS x"
echo "exit=$?"
type error: / by zero
{"error":{"code":"validation_error","exit_code":6,"kind":"type","message":"type error: / by zero","retryable":false}}
exit=6

A syntax error adds column and source, shown in the synopsis. A write on a --read-only store is a semantic error, also exit 6.

Exit 7: a timeout

relate --mode envelope --timeout 100ms "UNWIND range(1, 20000) AS a UNWIND range(1, 20000) AS b RETURN count(*) AS pairs"
echo "exit=$?"
Error: query timed out after 100ms
{"error":{"code":"timeout","exit_code":7,"kind":"execution","message":"query timed out after 100ms","retryable":false}}
exit=7

A multi-statement run that stops

When a run of several statements stops at a failure, stderr gains one line saying how far it got, and the error object gains statement, statements and committed:

relate --mode envelope "CREATE (:T {n: 1}); RETRN 2; RETURN 3 AS x"
echo "exit=$?"
{"columnTypes":[],"columns":[],"notifications":[],"planCache":"miss","rows":[],"timings":{"elapsed_ms":0.539084,"execute_ms":0.388542,"lex_parse_ms":0.003583,"lower_ms":0.003083,"parse_ms":0.1435,"plan_ms":0.001583,"result_generation_ms":0.000167,"rewrite_ms":0.00025},"truncated":false}
syntax error: parse error: unexpected token "RETRN"
  RETRN 2
  ^
statement 2 of 3 failed; 1 completed and committed; 1 not run
{"error":{"code":"validation_error","column":1,"committed":1,"exit_code":6,"kind":"syntax","message":"syntax error: parse error: unexpected token \"RETRN\"   RETRN 2   ^","retryable":false,"source":"RETRN 2","statement":2,"statements":3}}
exit=6

The first statement's write is committed; the third statement never ran. A run of several statements has no all-or-nothing flag, so committed is what a caller reads to know what changed.

See also