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
- Use a Code node (JavaScript) which supports require() for permitted modules, instead of an inline expression.
- If a helper value is needed in the expression, compute it upstream in a Set/Code node and reference the field via $json / $node[...].
- 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
- Lint for 'import(' in user expressions before save.
- Use a Code node for any module loading.
- Surface a helpful message pointing to the Code node alternative.
- Educate users that expressions are pure, module-less evaluators.
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
- Not a expression statement
- Cannot access "${name}" via expression extension due to secu
- LangSmithTelemetry creates its own tracer — do not use .otlp
- Invalid resume payload: ${parseResult.error}
- Episodic memory requires a resolved embedding model before r
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/03e724afdfd8ec3b.
Report an issue: GitHub.