Skip to content

Error codes

The engine emits a stable E_* code for every failure. Codes are part of the contract — the CLI, desktop, and JSON/JUnit output all report the same string, and you can assert on them.

Where a code shows up:

Terminal window
FAIL product.create (0ms) err=E_SESSION_REFRESH_FAILED
{ "op": "product.create", "status": "FAIL", "error_code": "E_SESSION_REFRESH_FAILED" }

Raised at load time, before any request. reqloom lint catches all of these.

CodeCauseFix
E_YAML_PARSEMalformed YAML, or a file over the 8 MiB capCheck indentation; the message names the file and line
E_SCHEMA_INVALIDStructurally valid YAML the engine can’t accept — commonly a bad hook pathRead the detail; it names the offending field
E_SCHEMA_VERSIONversion: is missing or outside 1–3Set a supported version:
E_REF_UNDEFINEDA {{X.y}} scope, or a depends_on target, doesn’t existFix the name — the message names the symbol
E_CYCLECircular dependencyBreak the loop; the message prints the path
Terminal window
LINT FAIL [E_CYCLE]: Circular dependency detected: order.one → order.two

Raised while building a request.

CodeCauseFix
E_VAR_UNRESOLVEDA reference couldn’t be substitutedMost often a typo’d extraction name, or a missing env var
E_UPLOAD_FILE_UNREADABLEA body_form @path is missing, not a regular file, or over 50 MiBCheck the path is relative to where you run the command
E_INDEXED_REF_OUT_OF_RANGEReserved — never raised. An out-of-range {{res[N].x}} surfaces as E_VAR_UNRESOLVED—

E_VAR_UNRESOLVED is the one you’ll meet most. Remember that lint validates the scope, not the field — {{order.typo}} passes lint whenever a resource named order exists, then fails here.

CodeCauseRetried
E_NETWORK_TIMEOUTConnect or read timeoutyes
E_NETWORK_DNSHostname didn’t resolveyes
E_NETWORK_TLSTLS handshake failedno

E_NETWORK_TLS against an internal host usually means a private CA — point transport.ca_bundle at it rather than disabling verification. See TLS settings.

CodeCauseRetried
E_HTTP_5XXServer erroryes
E_HTTP_4XXClient errorno
E_STATUS_MISMATCHStatus didn’t match expect_statusno

E_STATUS_MISMATCH with a 202 is the classic polling mistake: an async endpoint needs expect_status: [202, 200], not expect_status: 200. See polling.

CodeCauseFix
E_SESSION_REFRESH_FAILEDAn actor’s login or token refresh failedCheck credentials and the auth path; also raised when the server is unreachable during login
E_SECRET_ACCESS_FAILEDThe OS keychain couldn’t be readUnlock the keychain or grant access

A missing keychain entry is not E_SECRET_ACCESS_FAILED — the variable is left unresolved and you typically get a 401. Only a backend failure raises this.

CodeCauseFix
E_RESPONSE_PARSEResponse declared JSON but didn’t parseCheck the real Content-Type; an HTML error page is the usual culprit
E_EXTRACTION_FAILEDA JSONPath / XPath / regex matched nothingVerify the shape against a real response
E_ASSERTION_FAILEDAn assert: predicate was falseThe API changed, or the predicate is wrong
CodeCause
E_POLL_TIMEOUTpoll_until.timeout elapsed
E_POLL_MAX_ATTEMPTS_EXCEEDEDmax_attempts used up
E_POLL_FAIL_PREDICATEfail_when matched — the job reported failure

None are retried: the polling loop owns its own budget, so an outer retry would double it.

CodeCause
E_HOOK_FAILUREA pre_request / post_response script threw
E_HOOK_TIMEOUTA hook didn’t finish in time
CodeCause
E_LLM_REQUEST_FAILEDThe model call failed
E_LLM_RESPONSE_INVALIDThe model returned something unusable

Only reachable through the AI importer — never during a normal run.

CodeCause
E_CANCELLEDYou cancelled the run
E_INTERNALAn engine invariant broke — please report it
E_UNKNOWNUnclassified

Only three: E_NETWORK_TIMEOUT, E_NETWORK_DNS, and E_HTTP_5XX. Everything else fails immediately, because retrying a 401 or a failed assertion just wastes the budget.

Retries default to 3 attempts with 500 ms backoff, doubling per attempt. Tune it per operation:

retry:
max: 5
backoff: 1000

See timeouts and retries.

When a step fails, everything downstream is marked BLOCK with err=— — it never ran, so it has no error of its own:

Terminal window
FAIL product.create (0ms) err=E_SESSION_REFRESH_FAILED
BLOCK product.publish (0ms) err=—
BLOCK cart.add_item (0ms) err=—

Fix the first failure. In JUnit output, blocked and cancelled steps become <error> while failures become <failure>.

Codes are stable across releases, so they’re safe to grep:

Terminal window
reqloom run order.pay --format json --output run.json
jq -r '.steps[] | select(.error_code != null) | "\(.op) \(.error_code)"' run.json
Terminal window
product.create E_SESSION_REFRESH_FAILED