ErrLookup › Background articles › NodeOperationError in n8n: what the wrapped error means, and how to find the real cause behind it
NodeOperationError in n8n: what the wrapped error means, and how to find the real cause behind it
NodeOperationError is n8n's standard error class for a node-level operation failure: almost every error raised inside a node's execute path — API rejections, validation failures, invalid router dispatches, exceeded limits — either is one or gets re-wrapped into one with the node name and itemIndex attached. You meet it when a workflow execution halts (or, with Continue On Fail enabled, when an item comes back with error output), and the message you see may be the original error's text, a rewritten friendlier version of it, or a generic wrapper. The records also show mem0 surfacing the same class for server config validation, so exact semantics are library-specific.
Distilled from 226 documented records across 2 repositories.
Background
NodeOperationError sits at the node layer of n8n's execution engine. Individual nodes (and the shared router loops that drive them, e.g. in the Anthropic, AlibabaCloud, and Google Gemini nodes) run each input item through an operation function; when that function throws and Continue On Fail is off, the router catches the error and re-throws it as a NodeOperationError carrying the current itemIndex and, where present, the original error's description. This is the catch-all that guarantees every failure carries item-level context — the execution UI can point at the exact input item that failed. The original error's message typically becomes the NodeOperationError's message, so the first diagnostic step is always to read message and description plus the itemIndex in the error context.
From the caller's side the family looks different depending on which of four shapes produced it. First, the plain re-wrap: the message is verbatim the underlying failure (rate limit, auth, parse error), and the wrapper only adds context. Second, deliberate validation throws: the node checks its own parameters before doing work — an empty 'text' prompt in the OpenAi Assistant or Plan & Execute Agent nodes, missing 'Supply Data' code in the Code node, an empty embedding query — and halts with a specific message. Third, rewritten provider errors: the node pattern-matches an upstream API response and replaces it with a friendlier message — OpenAI's 'Expected file to have JSONL format' 400 becomes a JSONL explainer, a gateway's 'model not found' becomes a message naming the model and baseURL, Anthropic sampling-parameter deprecation 400s become instructions to remove Temperature/Top K/Top P, and ChromaDB dimension mismatches gain a remediation description. Fourth, router defaults: every resource/operation switch has a default branch that throws 'The operation/resource X is not supported', normally unreachable via the UI but reachable through hand-edited workflow JSON, stale node versions, or expressions that resolve to unexpected values.
Several records document quirks worth knowing. The 'operation not supported' messages in the Anthropic and Gemini routers actually interpolate the resource value, not the operation, so the reported value is not the thing that failed. The 'No binary data exists on item!' check in the Analyze Image operation is dead code — helpers.assertBinaryData() throws first. And because router catch blocks are untyped, a non-Error throw (a string or plain object) passed directly as the message can surface as '[object Object]'. The two mem0 records show the same class used for upfront config validation (rejecting LLM/embedder providers not bundled in the Docker image), a behavior specific to that library rather than n8n's item-processing model.
Common causes
- Underlying operation failure re-wrapped by the router.Any error thrown while processing an item — API failure, auth error, rate limit, parse error — is caught by the node's router loop and re-thrown as a NodeOperationError with itemIndex when Continue On Fail is disabled. The message is the original error's message, so root cause and fix live one level down.
- Empty or undefined required parameter.Prompt/text fields left blank, set to whitespace, or driven by an expression that resolves to undefined (a missing upstream field). Fires per-item inside the execute loop, so it can hit item 3 of 10 after earlier items succeeded. Seen in OpenAi Assistant, Plan & Execute Agent, Anthropic/Gemini analysis, Code node supplyData, and embedding query validation.
- Invalid resource/operation pair.A resource switch falls through to its default branch because the value came from hand-edited workflow JSON, a stale node version with renamed enums, or an expression resolving to an unknown string. Note the messages are often misleading — the Anthropic and Gemini variants interpolate the resource while claiming the operation is unsupported.
- Provider API rejection rewritten with context.The node detects a specific upstream error pattern and rewrites it: OpenAI's JSONL-format 400 on fine-tune uploads, gateway 'model not found' 404/400s, Anthropic 400s for deprecated sampling parameters, DashScope async task FAILED statuses. The rewrite explains the failure but the fix is on the provider side.
- Configuration drift between node and its dependencies.Saved state no longer matches reality: an embedding credential swapped so insert dimensions no longer match the ChromaDB collection, a stale genericAuthType after a node-definition change, a gateway baseURL that doesn't serve the selected model, or (in mem0) a provider not bundled in the Docker image.
- Exceeded limits and timeouts.Client-side or environment caps: binary passthrough to an agent exceeding the max size, more than 20 file_ids on an OpenAI assistant update, or an async video task still non-terminal after the polling budget (~60 attempts at 15s). The task-timeout variant is not necessarily a failure — the work simply outlasted the window.
- Agent/tool mismatches.A Conversational Agent invoking a tool that enforces a strict input schema the agent cannot satisfy, or an LLM returning a tool_call name that matches no connected tool (hallucinated, or tools changed mid-conversation).
- Dead-code or formatting quirks in the wrapper itself.Some checks are unreachable — e.g. the Analyze Image binary check fires after assertBinaryData has already thrown. And because catches are untyped, non-Error throws passed as the message can render as '[object Object]'.
What usually fixes it
- Read the full error, not just the class: message and description preserve the original failure, and error.context.itemIndex identifies the exact input item — inspect that item in the execution view before changing anything.
- Enable Continue On Fail in the node's Settings tab when you need the workflow past a failing item; errors are then captured in the output JSON instead of halting execution, which also makes batch diagnosis faster.
- Harden parameters upstream: give prompt/text expressions fallbacks ({{ $json.text || 'default' }}), filter items missing required fields with an IF node, and validate input data in a Code node before it reaches the failing node.
- Never hand-edit resource/operation values in workflow JSON — select them from the node's dropdowns, re-select them after importing workflows, and keep node versions updated so UI enums and router switches stay in sync.
- Align long-lived configuration: one embedding model per vector collection (clear or rename when switching), baseURL matched to the gateway's model catalogue, auth types re-picked after node upgrades, and (mem0) providers restricted to the bundled list or a custom image built for them.
- For async task timeouts, use the taskId in the error to query status manually, and shape requests (lower resolution, shorter duration, split tasks) to fit the polling window; for rewritten provider 400s, fix the payload the provider rejected (e.g. one JSON object per line for fine-tune files).
Go deeper
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Documented occurrences
- ${error.message}(n8n-io/n8n)
- The file "${fileName}" is ${sizeInMb} MB, which exceeds the ${limitInMb} MB limit for passing binary data to the model(n8n-io/n8n)
- ${error.message}(n8n-io/n8n)
- No binary data exists on item!(n8n-io/n8n)
- The operation "${operation}" is not supported!(n8n-io/n8n)
- ${error.message}. This is most likely because some of your tools are configured to require a specific schema. This is not supported by Conversational Agent. Remove the schema from the tool configuration or use Tools agent instead.(n8n-io/n8n)
- Task failed: [${errorCode}] ${errorMessage}(n8n-io/n8n)
- error?.description || error?.message(n8n-io/n8n)
- The ‘text‘ parameter is empty.(n8n-io/n8n)
- The type ${genericType} is not supported(n8n-io/n8n)
- No code for "Supply Data" set on node "${this.getNode().name}(n8n-io/n8n)
- Tool "${toolName}" was called but not found among connected tools(n8n-io/n8n)
- The resource "${resource}" is not supported!(n8n-io/n8n)
- The file content is not in JSONL format(n8n-io/n8n)
- ${error.message}(n8n-io/n8n)
- The model "${modelName}" does not support the Sampling Temperature, Top K, or Top P options. Remove them from Options and try again.(n8n-io/n8n)
- Task ${taskId} did not complete within the maximum polling time. Last status was not terminal. You can query the task manually using the task ID.(n8n-io/n8n)
- ChromaDB embedding dimension mismatch: ${displayMessage}(n8n-io/n8n)
- ${error.message}(n8n-io/n8n)
- Invalid JSON in "Metadata" field(mem0ai/mem0)
…and 206 more across the corpus — use search.
Honest provenance: generated on 2026-08-15 from AI-assisted analysis of the linked records. See how records are made.