n8n-io/n8n · error · UnsupportedNodeError

Unsupported syntax: '${nodeType}' is not allowed in SDK code

Error message

Unsupported syntax: '${nodeType}' is not allowed in SDK code

What it means

Thrown as UnsupportedNodeError (extends InterpreterError) by the SDK AST interpreter when a top-level statement is not VariableDeclaration, ExpressionStatement, or ExportDefaultDeclaration. The message lists the offending node type and, when source location is present, appends a code frame. This is a sandbox guard: only a restricted subset of JS is evaluable as SDK code.

Source

Thrown at packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.ts:83

	 * Interpret the AST program and return the result.
	 */
	interpret(ast: ESTree.Program): unknown {
		let result: unknown;

		for (const stmt of ast.body) {
			validateNodeType(stmt, this.sourceCode);

			switch (stmt.type) {
				case 'VariableDeclaration':
					this.visitVariableDeclaration(stmt);
					break;
				case 'ExpressionStatement':
					result = this.evaluate(stmt.expression);
					break;
				case 'ExportDefaultDeclaration':
					return this.evaluate(stmt.declaration as ESTree.Expression);
				default:
					throw new UnsupportedNodeError(stmt.type, stmt.loc ?? undefined, this.sourceCode);
			}
		}

		return result;
	}

	/**
	 * Process a variable declaration.
	 */
	private visitVariableDeclaration(node: ESTree.VariableDeclaration): void {
		// Only allow const declarations
		if (node.kind !== 'const') {
			throw new SecurityError(
				node.kind,
				node.loc ?? undefined,
				this.sourceCode,
				`'${node.kind}' declarations are not allowed. Use 'const' only.`,
			);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Restructure SDK code to use only const declarations, expression statements, and an optional export default.
  2. Move logic into allowed SDK function calls instead of defining helper functions at top level.
  3. Replace control-flow statements with expressions (e.g. ternaries, allowed SDK helpers).

Example fix

// before
function transform(x) { return x.toUpperCase(); }
export default transform(input);
// after
const transform = (x) => x.toUpperCase();
export default transform(input);
Defensive patterns

Strategy: validation

Validate before calling

import { parseSDKCode } from '@n8n/workflow-sdk';
import { UnsupportedNodeError } from '@n8n/workflow-sdk';

function preflightSDKCode(code: string): void {
  const ast = parseSDKCode(code); // throws on parse errors
  const allowed = new Set(['VariableDeclaration', 'ExpressionStatement', 'ExportDefaultDeclaration']);
  for (const stmt of ast.body) {
    if (!allowed.has(stmt.type)) {
      throw new Error(`SDK code may not contain '${stmt.type}'. Use only const declarations, expression statements, and an optional export default.`);
    }
  }
}

Type guard

import type { Program, Statement } from 'estree';

const ALLOWED_SDK_STATEMENTS = new Set<Statement['type']>([
  'VariableDeclaration',
  'ExpressionStatement',
  'ExportDefaultDeclaration',
]);

function isAllowedSdkStatement(stmt: Statement): boolean {
  return ALLOWED_SDK_STATEMENTS.has(stmt.type);
}

Try / catch

try {
  return interpret(code, sdkFunctions);
} catch (e) {
  if (e?.name === 'UnsupportedNodeError') {
    return { error: 'This SDK code uses unsupported syntax.', detail: e.message };
  }
  throw e;
}

Prevention

When it happens

Trigger: Authoring SDK code with a FunctionDeclaration, IfStatement, ForStatement, ReturnStatement at top level, a class declaration, or any other statement kind outside the allowed three. Also triggered by code that the parser accepts but the interpreter refuses (e.g. a try/catch block, a switch).

Common situations: Users pasting ordinary JS into an SDK code box expecting general execution; tooling that emits scaffolding (imports, functions) around the SDK snippet; attempting control flow or function definitions that the SDK vocabulary disallows.

Related errors


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