{"record":{"id":"5d99a29904185816","repo":"n8n-io/n8n","slug":"unsupported-syntax-node-type-forbidden-nod","errorCode":null,"errorMessage":"Unsupported syntax: '${node.type}: ${FORBIDDEN_NODE_TYPES[node.type]}' is not allowed in SDK code","messagePattern":"Unsupported syntax: '(.+?): (.+?)' is not allowed in SDK code","errorType":"exception","errorClass":"UnsupportedNodeError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/workflow-sdk/src/ast-interpreter/validators.ts","lineNumber":263,"sourceCode":"\tThrowStatement: 'Throw statements are not allowed in SDK code',\n\tWithStatement: 'With statements are not allowed in SDK code',\n\tUpdateExpression: 'Update expressions (++, --) are not allowed in SDK code',\n\tNewExpression: 'new expressions are not allowed. Use SDK factory functions instead.',\n\tImportDeclaration: 'Import declarations are not allowed in SDK code',\n\tImportExpression: 'Dynamic imports are not allowed in SDK code',\n\tExportNamedDeclaration: 'Named exports are not allowed. Use export default only.',\n\tExportAllDeclaration: 'Re-exports are not allowed in SDK code',\n\tAwaitExpression: 'Await expressions are not allowed in SDK code',\n\tYieldExpression: 'Yield expressions are not allowed in SDK code',\n};\n\n/**\n * Check if a node type is allowed.\n * @throws UnsupportedNodeError if the node type is not allowed\n */\nexport function validateNodeType(node: Node, sourceCode: string): void {\n\tif (FORBIDDEN_NODE_TYPES[node.type]) {\n\t\tthrow new UnsupportedNodeError(\n\t\t\t`${node.type}: ${FORBIDDEN_NODE_TYPES[node.type]}`,\n\t\t\tnode.loc ?? undefined,\n\t\t\tsourceCode,\n\t\t);\n\t}\n\n\tif (!ALLOWED_NODE_TYPES.has(node.type)) {\n\t\tthrow new UnsupportedNodeError(node.type, node.loc ?? undefined, sourceCode);\n\t}\n}\n\n/**\n * Check if an identifier is a dangerous global.\n * @throws SecurityError if the identifier is dangerous\n */\nexport function validateIdentifier(\n\tname: string,\n\t_allowedVariables: Set<string>,","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/workflow-sdk/src/ast-interpreter/validators.ts#L245-L281","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nexport default workflow().add(\n  node('LLM').parameters({ prompt: ($) => $.fromAi('text') })\n);\n\n// after\nexport default workflow().add(\n  node('LLM').parameters({ prompt: fromAi('text') })\n);","handlingStrategy":"validation","validationCode":"import { parseSDKCode } from '@n8n/workflow-sdk/ast-interpreter/parser';\nimport { FORBIDDEN_NODE_TYPES } from '@n8n/workflow-sdk/ast-interpreter/validators';\n\nfunction findForbiddenNodes(code: string): { type: string; remedy: string; line?: number }[] {\n  const hits: { type: string; remedy: string; line?: number }[] = [];\n  const ast = parseSDKCode(code);\n  JSON.stringify(ast, (k, v) => {\n    if (v && typeof v === 'object' && 'type' in v && FORBIDDEN_NODE_TYPES[(v as { type: string }).type]) {\n      const t = (v as { type: string; loc?: { start: { line: number } } }).type;\n      hits.push({ type: t, remedy: FORBIDDEN_NODE_TYPES[t], line: (v as { loc?: { start: { line: number } } }).loc?.start.line });\n    }\n    return v;\n  });\n  return hits;\n}","typeGuard":"import { FORBIDDEN_NODE_TYPES } from '@n8n/workflow-sdk/ast-interpreter/validators';\n\nfunction isForbiddenNode(type: string): boolean {\n  return type in FORBIDDEN_NODE_TYPES;\n}","tryCatchPattern":"import { interpretSDKCode } from '@n8n/workflow-sdk/ast-interpreter/interpreter';\nimport { UnsupportedNodeError } from '@n8n/workflow-sdk/ast-interpreter/errors';\n\ntry {\n  interpretSDKCode(code, sdkFunctions);\n} catch (e) {\n  if (e instanceof UnsupportedNodeError) {\n    // e.message contains the node type + remediation string from FORBIDDEN_NODE_TYPES\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["sdk","validators","forbidden-syntax","unsupported-syntax"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}