{"record":{"id":"4c570e4b852454af","repo":"n8n-io/n8n","slug":"failed-to-parse-workflow-code-error-message","errorCode":null,"errorMessage":"Failed to parse workflow code: ${error.message}","messagePattern":"Failed to parse workflow code: (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/workflow-sdk/src/codegen/parse-workflow-code.ts","lineNumber":631,"sourceCode":"\n\ttry {\n\t\t// Use AST interpreter instead of new Function() for security\n\t\tconst wf = interpretSDKCode(executableCode, sdkFunctions);\n\n\t\t// Return the JSON representation\n\t\treturn (wf as { toJSON: () => WorkflowJSON }).toJSON();\n\t} catch (error) {\n\t\tif (error instanceof SecurityError) {\n\t\t\t// Re-throw security errors with more context\n\t\t\tthrow new SyntaxError(\n\t\t\t\t`Failed to parse workflow code: ${error.message}. ` +\n\t\t\t\t\t'This code contains patterns that are not allowed for security reasons.',\n\t\t\t);\n\t\t}\n\t\tif (error instanceof InterpreterError) {\n\t\t\t// Check for reserved SDK name conflicts\n\t\t\tif (error.message.includes('reserved SDK function name')) {\n\t\t\t\tthrow new SyntaxError(`Failed to parse workflow code: ${error.message}`);\n\t\t\t}\n\t\t\t// Convert interpreter errors to syntax errors for consistent API\n\t\t\tthrow new SyntaxError(\n\t\t\t\t`Failed to parse workflow code: ${error.message}. ` +\n\t\t\t\t\t'Common causes include unclosed template literals, missing commas, or unbalanced brackets.',\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Parses generated TypeScript SDK code and returns the WorkflowBuilder.\n * This allows callers to validate the graph structure before converting to JSON.\n *\n * Uses a secure AST-based interpreter instead of eval/new Function().\n *\n * @param code - TypeScript code generated by generateWorkflowCode()","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/workflow-sdk/src/codegen/parse-workflow-code.ts#L613-L649","documentation":"Thrown by parseWorkflowCode when interpretSDKCode raises an InterpreterError whose message includes 'reserved SDK function name'. This specific case is re-thrown as a SyntaxError WITHOUT the 'common causes' hint, because it is a naming conflict, not a typo. Reserved names are the entries in ALLOWED_SDK_FUNCTIONS (workflow, node, trigger, ifElse, etc.).","triggerScenarios":"Declaring a const/variable with the same name as a reserved SDK function — e.g., `const node = 'foo'`, `const workflow = 123`, `const trigger = {}` — where the name is in the reserved set and not in AUTO_RENAMEABLE_SDK_FUNCTIONS (only subnode builder names like languageModel/memory/tool are auto-renamed).","commonSituations":"Using `node` as a variable name (very common — 'node' is a natural identifier); naming a variable `workflow` or `trigger`; refactoring that introduces a name collision with core SDK builders.","solutions":["Rename the local variable to something that is not a reserved SDK function name (e.g., `node` → `myNode`, `workflow` → `wf`).","Check ALLOWED_SDK_FUNCTIONS in validators.ts for the full reserved list; core builders (workflow, node, trigger, sticky, placeholder, newCredential) and control-flow (ifElse, switchCase, merge, splitInBatches, nextBatch) are NOT auto-renameable.","Subnode builder names (languageModel, memory, tool, etc.) ARE auto-renameable, so those will not trigger this error."],"exampleFix":"// before\nconst node = 'Http';\nexport default workflow().add(/* referencing node */);\n\n// after\nconst nodeName = 'Http';\nexport default workflow().add(node(nodeName));","handlingStrategy":"validation","validationCode":"import { ALLOWED_SDK_FUNCTIONS, AUTO_RENAMEABLE_SDK_FUNCTIONS } from '@n8n/workflow-sdk/ast-interpreter/validators';\n\nfunction findReservedNameCollisions(code: string): string[] {\n  const hardReserved = new Set(\n    [...ALLOWED_SDK_FUNCTIONS].filter((n) => !AUTO_RENAMEABLE_SDK_FUNCTIONS.has(n))\n  );\n  const hits = new Set<string>();\n  for (const name of hardReserved) {\n    const re = new RegExp(`\\\\b(?:const|let|var)\\\\s+${name}\\\\b`);\n    if (re.test(code)) hits.add(name);\n  }\n  return [...hits];\n}","typeGuard":"import { ALLOWED_SDK_FUNCTIONS, AUTO_RENAMEABLE_SDK_FUNCTIONS } from '@n8n/workflow-sdk/ast-interpreter/validators';\n\nfunction isHardReservedName(name: string): boolean {\n  return ALLOWED_SDK_FUNCTIONS.has(name) && !AUTO_RENAMEABLE_SDK_FUNCTIONS.has(name);\n}","tryCatchPattern":"import { parseWorkflowCode } from '@n8n/workflow-sdk/codegen/parse-workflow-code';\n\ntry {\n  parseWorkflowCode(code);\n} catch (e) {\n  if (e instanceof SyntaxError && /reserved SDK function name/.test(e.message)) {\n    // instruct user to rename the colliding local variable\n  }\n  throw e;\n}","preventionTips":["Never name a local variable `node`, `workflow`, `trigger`, `sticky`, `placeholder`, `ifElse`, `switchCase`, `merge`, `splitInBatches`, `nextBatch`, or `newCredential`.","Prefix local vars (my*, cfg*) to avoid collisions with SDK DSL keywords.","Keep ALLOWED_SDK_FUNCTIONS minus AUTO_RENAMEABLE_SDK_FUNCTIONS visible as the hard-reserved list."],"tags":["sdk","codegen","parse-workflow-code","reserved-name","naming-conflict"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}