n8n-io/n8n · error · UnsupportedNodeError
Unsupported syntax: '${node.type}: ${FORBIDDEN_NODE_TYPES[no
Error message
Unsupported syntax: '${node.type}: ${FORBIDDEN_NODE_TYPES[node.type]}' is not allowed in SDK code What it means
Thrown by validateNodeType when the AST node type is listed in FORBIDDEN_NODE_TYPES. Each forbidden type carries a remediation string (e.g., ArrowFunctionExpression → 'Use fromAi() directly instead of ($) => $.fromAi()'). The message includes both the node type and the remediation. This is the primary static-rejection gate for unsupported JavaScript constructs.
Source
Thrown at packages/@n8n/workflow-sdk/src/ast-interpreter/validators.ts:263
ThrowStatement: 'Throw statements are not allowed in SDK code',
WithStatement: 'With statements are not allowed in SDK code',
UpdateExpression: 'Update expressions (++, --) are not allowed in SDK code',
NewExpression: 'new expressions are not allowed. Use SDK factory functions instead.',
ImportDeclaration: 'Import declarations are not allowed in SDK code',
ImportExpression: 'Dynamic imports are not allowed in SDK code',
ExportNamedDeclaration: 'Named exports are not allowed. Use export default only.',
ExportAllDeclaration: 'Re-exports are not allowed in SDK code',
AwaitExpression: 'Await expressions are not allowed in SDK code',
YieldExpression: 'Yield expressions are not allowed in SDK code',
};
/**
* Check if a node type is allowed.
* @throws UnsupportedNodeError if the node type is not allowed
*/
export function validateNodeType(node: Node, sourceCode: string): void {
if (FORBIDDEN_NODE_TYPES[node.type]) {
throw new UnsupportedNodeError(
`${node.type}: ${FORBIDDEN_NODE_TYPES[node.type]}`,
node.loc ?? undefined,
sourceCode,
);
}
if (!ALLOWED_NODE_TYPES.has(node.type)) {
throw new UnsupportedNodeError(node.type, node.loc ?? undefined, sourceCode);
}
}
/**
* Check if an identifier is a dangerous global.
* @throws SecurityError if the identifier is dangerous
*/
export function validateIdentifier(
name: string,
_allowedVariables: Set<string>,View on GitHub (pinned to 5ac6606e81)
Solutions
- Replace arrow functions / callbacks with the direct SDK helper (e.g., use fromAi() instead of ($) => $.fromAi()).
- Replace loops with declarative data: build arrays via array literals, not iteration.
- Replace `new X(...)` with the corresponding SDK factory function (workflow/node/trigger/etc.).
- Remove try-catch, throw, await, yield — SDK builder code is synchronous and side-effect-free.
- Use a single `export default` — remove named exports and re-exports.
Example fix
// before
export default workflow().add(
node('LLM').parameters({ prompt: ($) => $.fromAi('text') })
);
// after
export default workflow().add(
node('LLM').parameters({ prompt: fromAi('text') })
); Defensive patterns
Strategy: validation
Validate before calling
import { parseSDKCode } from '@n8n/workflow-sdk/ast-interpreter/parser';
import { FORBIDDEN_NODE_TYPES } from '@n8n/workflow-sdk/ast-interpreter/validators';
function findForbiddenNodes(code: string): { type: string; remedy: string; line?: number }[] {
const hits: { type: string; remedy: string; line?: number }[] = [];
const ast = parseSDKCode(code);
JSON.stringify(ast, (k, v) => {
if (v && typeof v === 'object' && 'type' in v && FORBIDDEN_NODE_TYPES[(v as { type: string }).type]) {
const t = (v as { type: string; loc?: { start: { line: number } } }).type;
hits.push({ type: t, remedy: FORBIDDEN_NODE_TYPES[t], line: (v as { loc?: { start: { line: number } } }).loc?.start.line });
}
return v;
});
return hits;
} Type guard
import { FORBIDDEN_NODE_TYPES } from '@n8n/workflow-sdk/ast-interpreter/validators';
function isForbiddenNode(type: string): boolean {
return type in FORBIDDEN_NODE_TYPES;
} Try / catch
import { interpretSDKCode } from '@n8n/workflow-sdk/ast-interpreter/interpreter';
import { UnsupportedNodeError } from '@n8n/workflow-sdk/ast-interpreter/errors';
try {
interpretSDKCode(code, sdkFunctions);
} catch (e) {
if (e instanceof UnsupportedNodeError) {
// e.message contains the node type + remediation string from FORBIDDEN_NODE_TYPES
}
throw e;
} Prevention
- Keep FORBIDDEN_NODE_TYPES handy and lint against it.
- Replace arrow functions with fromAi() / direct SDK calls.
- Avoid loops; build arrays with literals. Avoid try/catch, throw, await, yield, ++/--, new, and non-default imports/exports.
When it happens
Trigger: Using arrow functions (ArrowFunctionExpression), function declarations/expressions, classes, for/while/do-while/for-in/for-of loops, try-catch, throw, update expressions (++/--), new expressions, import/export declarations (except default), await, or yield in SDK code.
Common situations: Pasting a standard JS function into SDK code; using `($) => $.fromAi()` instead of `fromAi()`; writing loops to build node arrays; using `++` in an index; `new Array(...)`; `await` in async-flavored code; named exports.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unsupported syntax: '${node.type}' is not allowed in SDK cod
- Security violation: '${name}' is not allowed
- Security violation: 'eval()' is not allowed
- Security violation: 'Function()' is not allowed
- Security violation: 'require()' is not allowed
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/5d99a29904185816.
Report an issue: GitHub.