6.7 KiB
Code Tool Error Patterns
The most common failure modes for @n8n/n8n-nodes-langchain.toolCode, with exact error strings, root causes, and fixes.
Error 1: "Cannot assign to read only property 'name' of object: Error: No execution data available"
Full message (wrapped by n8n):
There was an error: "Cannot assign to read only property 'name' of object 'Error: No execution data available'"
Cause: Calling $fromAI() inside the Code Tool sandbox. $fromAI() is a helper intended for other tool-enabled nodes (HTTP Request Tool, SendGrid Tool, toolWorkflow) where AI-supplied values flow through workflow execution data. The Code Tool sandbox has no execution data — it receives input directly via query. The helper throws, n8n tries to annotate the error's name property, and that assignment fails because the error object is frozen.
Fix: remove $fromAI(). Read from query (or define an input schema, see INPUT_SCHEMA.md).
// ❌ Broken
const price = $fromAI('price', 'Car price in SEK', 'number');
// ✅ Unstructured — parse a JSON string
const params = JSON.parse(query);
const price = Number(params.price);
// ✅ Structured — with specifyInputSchema: true
const { price } = query;
Error 2: "Wrong output type returned"
Cause: You returned the workflow item format ([{json: {...}}]) from the Code Tool. That format is for regular Code nodes; tools follow the LangChain contract and must return a string.
Fix: return a string. For structured output, stringify:
// ❌ Broken
return [{ json: { monthly_payment: 5405 } }];
// ✅ Fixed
return JSON.stringify({ monthly_payment: 5405 });
Error 3: "The response property should be a string, but it is an <type>"
Where <type> is object, undefined, function, etc.
Cause: You returned a bare object, array, or nothing at all.
| Returned value | Error says | Fix |
|---|---|---|
{ result: 42 } |
...is an object |
JSON.stringify({ result: 42 }) |
[1, 2, 3] |
...is an object |
JSON.stringify([1, 2, 3]) |
(no return) |
...is an undefined |
Add a return |
undefined |
...is an undefined |
Return something |
Numbers are fine — n8n auto-converts them to strings:
return 42; // ✅ becomes "42"
Booleans are NOT auto-converted — stringify explicitly:
return String(someBoolean); // ✅
return JSON.stringify(someBoolean); // ✅
Error 4: AI never calls the tool
Symptom: the agent answers from its own reasoning and ignores the tool. No tool invocation shows up in the execution trace.
Common causes and fixes:
-
Generic name. Default names like
Code ToolorMy Toolgive the LLM no signal.- Fix: rename to verb-y, domain-specific snake_case:
calculate_car_loan,search_orders,lookup_customer.
- Fix: rename to verb-y, domain-specific snake_case:
-
Description doesn't state the trigger. "Calculates things" is too vague.
- Fix: explicitly list the user intents that should invoke the tool.
"Use this whenever the user asks about monthly cost, loan breakdown, or total interest."
- Fix: explicitly list the user intents that should invoke the tool.
-
Tool isn't wired. The node sits in the canvas but isn't connected to the AI Agent's
ai_toolinput.- Fix: connect it. Check the workflow JSON
connectionsblock has"<tool_name>": { "ai_tool": [[{ "node": "AI Agent", "type": "ai_tool", "index": 0 }]] }.
- Fix: connect it. Check the workflow JSON
-
Name violates
[A-Za-z0-9_]+. Spaces, hyphens, and emoji in the tool name cause silent skip on v1.1+.- Fix: rename to
snake_case_only.
- Fix: rename to
Error 5: LLM sends malformed query
Symptom: your JSON.parse(query) throws, or fields come through as wrong types.
Causes:
- You're in unstructured mode and the description is ambiguous, so the LLM invents a format.
- You asked for a JSON string but the LLM sent a natural-language sentence.
- Numeric fields arrive as strings because the LLM serialized them that way.
Fixes, in order of preference:
-
Switch to structured mode. Set
specifyInputSchema: trueand define fields. The LLM now gets a typed schema and n8n validates before your code runs. -
Give a concrete example in the description. LLMs imitate examples well:
Call with a single JSON string. Example: {"price":439900,"down_payment":87980,"interest_rate":6.95} -
Coerce defensively:
const params = JSON.parse(query); const price = Number(params.price); if (!isFinite(price)) throw new Error('price must be numeric');
Error 6: "$helpers is not defined" / "$input is not defined"
Cause: you assumed the Code Tool sandbox exposes the same helpers as the Code node. It doesn't.
Unavailable in Code Tool:
$input,$json,$binary$node["OtherNode"]$helpers.httpRequest()$jmespath()this.getContext(...),$getWorkflowStaticData(...)$fromAI()
Fix:
- Pure computation? Stay in Code Tool, use plain JS.
- Need HTTP? Move to HTTP Request Tool (with
$fromAI()in URL/body). - Need other-node data or credentials? Move to Call Sub-workflow Tool (
toolWorkflow) — its sub-workflow has a full Code node sandbox. - Need state across calls? Not possible in Code Tool. Use a sub-workflow that reads/writes a Data Table, Redis, etc.
Error 7: Python-specific — "name 'query' is not defined"
Cause: in Python, the input variable is _query (underscore prefix), not query.
# ❌ Broken
result = process(query)
# ✅ Fixed
result = process(_query)
Error Prevention Checklist
Before saving a Code Tool:
- Tool name is snake_case, descriptive, and unique
- Description tells the LLM when to call it, with an example if unstructured
- No
$fromAI()in the code body - No
$input,$json,$helpers— not in this sandbox - Input read from
query(JS) or_query(Python) - All code paths
returna string (or a number that auto-converts) - If returning structured data, wrapped in
JSON.stringify(...) - Wired to an AI Agent via
ai_toolconnection - For multi-field input: either example JSON in description, or
specifyInputSchema: true
Debugging tips
- Use the Execution view, not just the test output. The agent's tool invocation and raw input/output are visible there — you can see exactly what
querythe LLM sent. - Log inside the tool by including fields in the returned JSON:
The LLM sees the echo, and you can spot malformed input.
return JSON.stringify({ received_query: query, result: /* ... */ }); - Test the tool without the LLM by temporarily turning the tool node into a standalone Code node with hard-coded
query, running it manually, then swapping back.