n8n-io/n8n · error · SyntaxError

Failed to parse workflow code: ${error.message}

Error message

Failed to parse workflow code: ${error.message}

What it means

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.).

Source

Thrown at packages/@n8n/workflow-sdk/src/codegen/parse-workflow-code.ts:631

	try {
		// Use AST interpreter instead of new Function() for security
		const wf = interpretSDKCode(executableCode, sdkFunctions);

		// Return the JSON representation
		return (wf as { toJSON: () => WorkflowJSON }).toJSON();
	} catch (error) {
		if (error instanceof SecurityError) {
			// Re-throw security errors with more context
			throw new SyntaxError(
				`Failed to parse workflow code: ${error.message}. ` +
					'This code contains patterns that are not allowed for security reasons.',
			);
		}
		if (error instanceof InterpreterError) {
			// Check for reserved SDK name conflicts
			if (error.message.includes('reserved SDK function name')) {
				throw new SyntaxError(`Failed to parse workflow code: ${error.message}`);
			}
			// Convert interpreter errors to syntax errors for consistent API
			throw new SyntaxError(
				`Failed to parse workflow code: ${error.message}. ` +
					'Common causes include unclosed template literals, missing commas, or unbalanced brackets.',
			);
		}
		throw error;
	}
}

/**
 * Parses generated TypeScript SDK code and returns the WorkflowBuilder.
 * This allows callers to validate the graph structure before converting to JSON.
 *
 * Uses a secure AST-based interpreter instead of eval/new Function().
 *
 * @param code - TypeScript code generated by generateWorkflowCode()

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Rename the local variable to something that is not a reserved SDK function name (e.g., `node` → `myNode`, `workflow` → `wf`).
  2. 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.
  3. Subnode builder names (languageModel, memory, tool, etc.) ARE auto-renameable, so those will not trigger this error.

Example fix

// before
const node = 'Http';
export default workflow().add(/* referencing node */);

// after
const nodeName = 'Http';
export default workflow().add(node(nodeName));
Defensive patterns

Strategy: validation

Validate before calling

import { ALLOWED_SDK_FUNCTIONS, AUTO_RENAMEABLE_SDK_FUNCTIONS } from '@n8n/workflow-sdk/ast-interpreter/validators';

function findReservedNameCollisions(code: string): string[] {
  const hardReserved = new Set(
    [...ALLOWED_SDK_FUNCTIONS].filter((n) => !AUTO_RENAMEABLE_SDK_FUNCTIONS.has(n))
  );
  const hits = new Set<string>();
  for (const name of hardReserved) {
    const re = new RegExp(`\\b(?:const|let|var)\\s+${name}\\b`);
    if (re.test(code)) hits.add(name);
  }
  return [...hits];
}

Type guard

import { ALLOWED_SDK_FUNCTIONS, AUTO_RENAMEABLE_SDK_FUNCTIONS } from '@n8n/workflow-sdk/ast-interpreter/validators';

function isHardReservedName(name: string): boolean {
  return ALLOWED_SDK_FUNCTIONS.has(name) && !AUTO_RENAMEABLE_SDK_FUNCTIONS.has(name);
}

Try / catch

import { parseWorkflowCode } from '@n8n/workflow-sdk/codegen/parse-workflow-code';

try {
  parseWorkflowCode(code);
} catch (e) {
  if (e instanceof SyntaxError && /reserved SDK function name/.test(e.message)) {
    // instruct user to rename the colliding local variable
  }
  throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/4c570e4b852454af. Report an issue: GitHub.