n8n-io/n8n · error · Error

Imports are not supported

Error message

Imports are not supported

What it means

Error('Imports are not supported') is thrown by jsVariablePolyfill's visitor when it encounters a visitImportExpression, i.e. a dynamic import() call inside an n8n expression. Static import statements would have already failed parsing for an expression context, so this guard specifically catches the dynamic form.

Source

Thrown at packages/@n8n/tournament/src/VariablePolyfill.ts:128

	},
	ArrowFunctionExpression(path, parent: namedTypes.ArrowFunctionExpression, dataNode) {
		// A concise arrow body that is a bare identifier (`() => process`) must be
		// routed through the data context like any other free read. Params are not
		// the body, and body identifiers that reference a param are left alone by
		// polyfillVar's in-scope check.
		if (parent.body === path.node) {
			polyfillVar(path, dataNode);
		}
	},
};

export const jsVariablePolyfill = (
	ast: types.namedTypes.File,
	dataNode: DataNode,
): StatementKind[] | undefined => {
	visit(ast, {
		visitImportExpression(_path) {
			throw new Error('Imports are not supported');
		},
		visitIdentifier(path) {
			this.traverse(path);
			const parent: ParentKind = path.parent.node;

			// This is for tmpl compat
			if (EXEMPT_IDENTIFIER_LIST.includes(path.node.name)) {
				return;
			}

			switch (parent.type) {
				case 'AssignmentPattern':
				case 'Property':
				case 'MemberExpression':
				case 'OptionalMemberExpression':
				case 'VariableDeclarator':
				case 'ArrowFunctionExpression':
					if (!customPatches[parent.type]) {

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Use a Code node (JavaScript) which supports require() for permitted modules, instead of an inline expression.
  2. If a helper value is needed in the expression, compute it upstream in a Set/Code node and reference the field via $json / $node[...].
  3. Remove any import() call from inside {{ }}.

Example fix

// before
{{ (await import('lodash')).get($json, 'a.b') }}
// after
# In a Code node upstream:
#   const _ = require('lodash');
#   return [{ json: { val: _.get($input.all()[0].json, 'a.b') } }];
# Then in the expression: {{ $json.val }}
Defensive patterns

Strategy: validation

Validate before calling

// Reject dynamic import() in expressions at authoring time.
if (/\\bimport\\s*\\(/.test(codeChunk)) {
  throw new Error('dynamic import() is not supported in n8n expressions');
}

Type guard

import { namedTypes } from 'ast-types';
function isImportExpression(node: unknown): node is namedTypes.ImportExpression {
  return !!node && (node as any).type === 'ImportExpression';
}

Try / catch

try {
  const compiled = buildExpression(chunks, hooks);
} catch (e) {
  if (e instanceof Error && /Imports are not supported/.test(e.message)) {
    throw new UserError('import() is blocked in expressions; use a Code node with require().');
  }
  throw e;
}

Prevention

When it happens

Trigger: An n8n expression chunk contains a dynamic import, e.g. '{{ import("lodash") }}' or '{{ await import("./helper") }}'.

Common situations: A user expects to load a library on the fly from an expression; a pasted code snippet uses dynamic import for code splitting; a user attempts to reach Node built-ins via import.

Related errors


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