n8n-io/n8n · error · Error
Unable to parse function body
Error message
Unable to parse function body
What it means
Thrown by extractFunctionBody in code-helpers.ts when Function.prototype.toString() of the supplied callback matches neither the arrow-function regex nor the `function(ctx)` regex. The helpers runOnceForAllItems / runOnceForEachItem convert a TS closure into a Code-node source string by stringifying the function and rewriting the ctx. parameter; anything that diverges from the two supported shapes cannot be transformed.
Source
Thrown at packages/@n8n/workflow-sdk/src/utils/code-helpers.ts:48
return body;
}
// Handle regular functions: function(ctx) { ... }
const funcMatch = fnStr.match(/function\s*\(\s*(\w+)\s*\)\s*\{([\s\S]*)\}$/);
if (funcMatch) {
const paramName = funcMatch[1];
let body = funcMatch[2].trim();
// Replace parameter name
body = body.replace(new RegExp(`${paramName}\\.\\$`, 'g'), '$');
body = body.replace(new RegExp(`${paramName}\\(`, 'g'), '$(');
body = body.replace(new RegExp(`${paramName}\\.`, 'g'), '');
return body;
}
throw new Error('Unable to parse function body');
}
/**
* Create a code helper for executing once with access to all items
*
* The function receives a context object with:
* - ctx.$input.all() - get all input items
* - ctx.$input.first() - get first input item
* - ctx.$input.last() - get last input item
* - ctx.$input.itemMatching(i) - get item at index
* - ctx.$env, ctx.$vars, ctx.$secrets - environment access
* - ctx.$now, ctx.$today - date helpers
* - ctx.$execution, ctx.$workflow - metadata
* - ctx('NodeName') - reference other node outputs
*
* @param fn - Function that processes all items and returns an array
* @returns Code configuration for the Code node
*View on GitHub (pinned to 5ac6606e81)
Solutions
- Pass an arrow function (ctx) => { ... } or a named function(ctx) { ... } with exactly one parameter.
- Avoid .bind() — capture closure variables directly inside the arrow body.
- In bundler setups, mark the callback as non-mangled (function names preserved) or inline the source string.
Example fix
// before
runOnceForAllItems(this.processItems.bind(this));
// after
runOnceForAllItems((ctx) => {
const items = ctx.$input.all();
return [{ json: { sum: items.length } }];
}); Defensive patterns
Strategy: validation
Validate before calling
function isSupportedCodeHelperFn(fn: unknown): boolean {
if (typeof fn !== 'function') return false;
const s = fn.toString();
return /^\s*\(?\w+\)?\s*=>\s+.+$/s.test(s) || /function\s*\(\s*\w+\s*\)\s*\{[\s\S]*\}$/.test(s);
}
if (!isSupportedCodeHelperFn(cb)) {
throw new Error('Pass an arrow (ctx) => {...} or function(ctx) {...} with one parameter');
} Type guard
function isArrowOrNamedFunction(ctx: unknown, fn: unknown): fn is (ctx: any) => any {
if (typeof fn !== 'function') return false;
const s = fn.toString();
return /^\s*\(?\w+\)?\s*=>/.test(s) || /^\s*function\s*\(\s*\w+\s*\)/.test(s);
} Try / catch
try {
runOnceForAllItems(cb as (ctx: AllItemsContext) => any);
} catch (e) {
if (e instanceof Error && e.message === 'Unable to parse function body') {
throw new Error('Code helper could not stringify the callback; pass an un-bound arrow or function(ctx).');
}
throw e;
} Prevention
- Always pass an inline arrow (ctx) => { ... } to runOnceForAllItems / runOnceForEachItem.
- Never pass a .bind() result or a method reference.
- Configure your bundler to preserve function names/source for files containing code helpers.
When it happens
Trigger: Passing a generator function (function*), an async function whose stringified form carries modifiers the regex skips, a bound function (.bind()), a method reference, or a zero-argument function. Minified/transpiled builds that mangle fn.toString() also fail the match.
Common situations: Production bundlers rename or inline functions so fn.toString() no longer matches `(ctx) => ...`; developer passes a class method `this.handler.bind(this)`; using an async generator for streaming-style code.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${toolName} was registered without a runSubAgent callback, a
- No cancellation handler is available for "${request.subAgent
- No monitors available
- Failed to parse agent steps
- ${error.message}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/6fbc4932c884b235.
Report an issue: GitHub.