---
title: "Error catalog"
description: "Every user-facing Axiom error — flow run failures in the editor, edge transform errors, and HTTP API error responses — with what causes each one and how to fix it."
category: reference
surfaces: [canvas, http-api, console]
related: [getting-started/invoke-via-api, guides/debug-a-flow, reference/http-api, concepts/execution-model, guides/api-keys]
last_reviewed: 2026-07-06
---

# Error catalog

This page lists the errors Axiom shows users, grouped by where you see them:
in the editor when a flow run fails, and in HTTP responses when you call the
API. Each entry gives the exact error shape, the cause, and the fix.

## How errors name nodes and edges

Flow run errors identify nodes by their **canvas id** — the per-placement id
you see on the node card in the editor (for example `id-bad` or
`node-1716492048192`) — never by the marketplace node name. This matters
when a flow contains two placements of the same node: the error names the
specific placement that failed, so you can find it on the canvas directly.

Edges are named by quoting both endpoints. An edge error reads:

```text
edge "id-bad" → "echo-merge": EvaluateEdge adapter: transform TRANSFORM_PREFIX failed: prefix needs a string to prepend, e.g. prefix("hi ")
```

Here `id-bad` and `echo-merge` are the canvas ids of the edge's source and
destination nodes.

When a failure is attributed to a specific edge or node, the result panel at
the bottom of the editor also shows the failure kind (for example
`EDGE_ADAPTER`) and a **Show on canvas** button. Clicking it selects the
offending edge — or node, when the failure has no edge attribution — on the
canvas so you can open and fix it. See
[Debug a flow](../guides/debug-a-flow.md) for the full debugging workflow.

## Flow run errors in the editor

When an execution fails, the flow status pill above the canvas shows
**Failed**, the result panel header shows **Execution Failed**, and the
panel's **Output** tab contains the error text. The errors you can see:

| Error | Cause | Fix |
|---|---|---|
| `edge "<src>" → "<dst>": EvaluateEdge adapter: <detail>` | The edge's adapter (a bare CEL expression) failed to compile or evaluate at runtime — a parse error, a type mismatch, or an eval error. | Open the edge and fix the CEL expression. See [Edge adapter (CEL) errors](#edge-adapter-cel-errors). |
| `edge "<src>" → "<dst>": EvaluateEdge condition: <detail>` | The edge's condition expression failed to evaluate (for example `CEL evaluation failed: …`). | Fix the condition expression on that edge. |
| `invalid JSON for <InputType>: <detail>` | The JSON input you submitted does not match the entry node's input message — unknown field, wrong type. | Match the field names and types of the entry node's input message. See [Type system](../concepts/type-system.md). |
| `graph produced no terminal result` | The execution ended without the terminal node producing output — typically every outgoing edge condition evaluated to false somewhere along the path. | Check edge conditions; make sure at least one path reaches the terminal node for this input. |
| `exceeded max steps (1000)` | The execution dispatched more than 1000 node steps — almost always a loop that never meets its exit condition. | Fix the loop's exit condition. |
| `... daily executions quota exceeded (cap <n>)` | The flow tried to spawn child executions (parallel branches, runtime mutation) after the [daily execution quota](../reference/http-api.md#daily-quotas) ran out; the whole flow fails. | Wait for the midnight-UTC reset, or ask the operator for a higher limit. |
| `downstream join <n> failed: <reason>` | A node feeding a join failed, and the join's failure policy aborted the flow. | Fix the failing branch, or relax the join's failure policy. |
| `open pipeline stream for node "<id>"`, `send to node "<id>"`, `recv from node "<id>"` | In pipeline mode, the platform lost the connection to that node's container. | Usually transient — re-run; if it persists, check the node's logs. |

When your own node code raises an exception, the handler's error message is
surfaced verbatim: it appears on the `NODE_FAILED` event in the result
panel's **Execution** tab, attributed to the failing placement's canvas id.

## Edge adapter (CEL) errors

An edge adapter's right-hand side is a bare CEL expression (or a plain field
pick) evaluated against the source message. A failing expression produces a
CEL error inside an edge error (see above). The failure modes:

| Stage | Error | Meaning |
|---|---|---|
| Parse | `CEL parse error: <detail>` | The expression is not syntactically valid CEL. |
| Type-check / compile | `CEL compilation failed: <detail>` (e.g. no matching overload, undeclared reference) | The expression is well-formed but ill-typed — e.g. `text + 1` mixes string and int, or it references a field/function that is not in scope. |
| Eval | `CEL evaluation failed: <detail>` | The expression compiled but failed against this input — e.g. an index out of range or a division by zero. |

CEL string helpers such as `toUpper`, `toLower`, `trim`, `size`, and `join`
are available in the adapter scope; call them directly (`toUpper(text)`), not
through a pipe. A destination field path (the mapping *key*) that contains
`(` or `)` is rejected at compile time as a malformed field name.

**Whole message-typed field picks** copy the message byte-for-byte, so they
are only valid between the **same** message type. These are rejected at
**compile time** (never at run) — the fix is to reconstruct the destination in
CEL, field by field:

| Error | Meaning | Fix |
|---|---|---|
| `adapter: field pick "<src>" maps message type "<A>" into differently-typed message field "<dst>" (type "<B>") …` | A whole-message pick across two *different* message types — a verbatim byte copy would misread the wire. | Map each leaf field explicitly in CEL (`"<dst>.field": "<src>.field"`), or use a CEL object literal, so the reshape is declared. |
| `adapter: field pick "<src>" maps a <kind> value into <kind> field "<dst>" … cannot convert between a message and a scalar …` | A pick tried to bridge a message field and a scalar field. | Write a CEL expression that constructs the destination shape. |
| `adapter: field pick "<src>" maps a <repeated/singular> message into a <singular/repeated> field "<dst>" … requires matching cardinality …` | A whole-message pick with mismatched cardinality (a repeated message into a singular field, or vice versa). | Reshape in CEL (e.g. index a single element, or `map(…)` a list). |

## HTTP API errors when invoking a flow

Errors from `POST /invocations/v1/flows/invoke` and its streaming variant.
For the full request/response contract see the
[HTTP API reference](../reference/http-api.md).

### Authentication, rate limiting, and quotas

| Status | Body | Cause | Fix |
|---|---|---|---|
| 401 | `{"error":"unauthorized"}` | Missing, malformed, revoked, or expired credentials on any authenticated route. | Send a valid key in the `Authorization: Bearer` header — see [Create and manage API keys](../guides/api-keys.md). |
| 429 | `{"error":"rate limit exceeded"}` | Your tenant exceeded the per-tenant invocation rate limit (per-second burst). The response carries a `Retry-After: 1` header. | Back off and retry after the indicated delay. |
| 429 | `{"error":"quota_exceeded", "retry_after_seconds": ...}` | Your tenant exhausted one of its [daily quotas](../reference/http-api.md#daily-quotas) — invocations or executions. The `message` names which; `retry_after_seconds` counts down to the reset at midnight UTC. | Wait for the daily reset, or ask the operator to raise your tenant's limit. |
| 403 | `{"error":"tenant_suspended"}` | Your tenant has been disabled by the operator. All invocations and resumes are rejected. | Contact the operator. |
| 503 | `{"error":"quota_unavailable", "retry_after_seconds": 5}` | The quota service could not be reached, so the request was rejected as a safety measure. | Transient — retry after the indicated delay. |

### Structured invoke errors

Invoke failures with a known cause return a structured body:

```json
{
  "error": "payload_too_large",
  "message": "payload exceeds hard cap: actual=17825792 max=16777216",
  "max_bytes": 16777216,
  "actual_bytes": 17825792
}
```

| Status | `error` class | Cause | Fix |
|---|---|---|---|
| 413 | `payload_too_large` | The input exceeds the 16 MiB hard cap (`max_bytes`/`actual_bytes` populated). | Shrink the input payload. |
| 503 | `upstream_unavailable` | The flow could not be queued; a platform dependency was temporarily unavailable. `retry_after_seconds: 5`. | Transient — retry after the indicated delay. |
| 503 | `blob_storage_unavailable` | A large input payload could not be stored; a platform dependency was temporarily unavailable. `retry_after_seconds: 5`. | Transient — retry after the indicated delay. |

Failures that don't classify return `500` with
`{"accepted": false, "error_message": "<detail>"}`.

### Timeouts

With `"wait": true`, if the execution does not complete within the timeout
(default 30 seconds), the response is still `202` — the execution keeps
running:

```json
{
  "accepted": true,
  "execution_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "error_message": "timeout waiting for flow result (30s)"
}
```

Set `"timeout_seconds"` to wait longer, or look the `execution_id` up later
in execution history. On the streaming endpoint, a timeout ends the stream
with a final frame whose `error` is `"timeout waiting for pipeline result"`;
a flow that fails mid-stream ends with a final frame whose `error` carries
the flow run error described above.

A request that is accepted and delivered but whose execution runs to a
failure (for example, the input did not match the flow's start-node schema)
returns HTTP `202` with `"result":{"success":false,...}` and surfaces the
worker's diagnostic in both `result.error_message` and the top-level
`error_message` — so a synchronous caller can tell a delivered failure apart
from a timeout or transport error without polling execution history.

## HTTP API errors when invoking a single node

Errors from the per-node JSON endpoint
`POST /invocations/v1/nodes/{owner}/{pkg}/{version}/{node}` (also accepts a
node id in place of the name path):

| Status | Body | Cause | Fix |
|---|---|---|---|
| 404 | `{"error_message":"node not found or not yet deployed"}` (or `node not found: <detail>` for a name path that doesn't resolve) | The node id or `{owner}/{pkg}/{version}/{node}` path doesn't match a published, deployed node. | Check the package name, version, and node name against the marketplace listing. |
| 400 | `{"error_message":"invalid JSON for <InputType>: <detail>"}` | The JSON body doesn't match the node's input message. | Match the input message's field names and types. |
| 400 | `{"execution_id":"...","error_message":"invalid value for <InputType>: field \"<name>\" carries enum ordinal <n>, which names no declared value ..."}` | An enum field is set to an ordinal that is not a declared value of its enum — typically a payload recorded before the field was an enum (or against a different schema version). | Re-record the payload against the node's current schema. |
| 422 | `{"error_message":"<detail>"}` | The node ran and its handler returned an error — `error_message` is the handler's own message. | Fix the input, or the node code. |
| 502 | `{"error_message":"could not connect to node"}` / `"calling node: <detail>"` / `"sending to node: <detail>"` | The platform could not reach the node's container. | Usually transient (cold start) — retry; if it persists, check the node's deployment. |

The binary-protobuf variant `POST /invocations/v1/nodes/invoke` validates its
base64 `payload` against the node's current input schema before forwarding:
bytes whose wire shape doesn't match (an undeclared field number, or a wire
type conflicting with the declared kind), or any payload whose node schema
can't be resolved, return HTTP 400 with
`{"success":false,"error_message":"payload does not match the node's current input schema: <detail>"}`.

## HTTP API errors when pre-warming a flow

Errors from `POST /invocations/v1/flows/warm` (see
[Pre-warm a flow](../reference/http-api.md#pre-warm-a-flow)). Authentication,
rate-limit, and quota failures return the same bodies as
[invoking a flow](#authentication-rate-limiting-and-quotas) above — a warm
debits the same daily invocation quota an invoke does. Pre-warm adds these
ownership and availability cases:

| Status | Body | Cause | Fix |
|---|---|---|---|
| 400 | `{"error":"invalid_request","message":"..."}` | `graph_id` is missing or doesn't match `[A-Za-z0-9_-]+`. | Pass a valid `graph_id`. |
| 403 | `{"error":"forbidden","message":"not authorized to warm this flow"}` | The authenticated tenant does not own `graph_id`. | Warm a flow your tenant owns; a foreign `graph_id` is never accepted, even to pre-warm it. |
| 404 | `{"error":"not_found","message":"flow not found"}` | `graph_id` doesn't resolve to any flow. | Check the id against the flow's **Use via API** dialog. |
| 503 | `{"error":"warm_unavailable","message":"...","retry_after_seconds":5}` | The warm dispatcher isn't configured on this deployment. | Transient/deployment-specific — retry after the indicated delay, or invoke the flow directly instead. |

## Execution history and debug API errors

Errors from the execution-history and debugging endpoints (used by
[Debug a flow](../guides/debug-a-flow.md)):

- Execution history and event requests return `404` with
  `{"error":"execution not found"}` when the execution id doesn't exist
  for your tenant.
- `GET /invocations/v1/executions/{id}/checkpoints/{checkpoint_id}` returns
  `404` with
  `{"error":"checkpoint_not_found_or_expired","detail":"checkpoint may have been evicted by TTL policy (default 30d)"}`
  — checkpoints expire after a retention window (default 30 days), so debug
  a recent execution instead.
- Agent memory operations return standard statuses: `404` when a memory
  entry or session is not found, `400` for invalid arguments, `403` for
  permission denied, `401` for unauthenticated requests, and `429` when
  resource limits are exhausted.

All endpoints are tenant-scoped: a valid id belonging to another tenant
behaves exactly like a nonexistent one (`404`), never `403` — see
[Sandboxing and tenancy](../concepts/sandboxing-and-tenancy.md).
