n8n-io/n8n · error · ExpressionExtensionError

Cannot access " " via expression extension due to security…

Error message

Cannot access "${name}" via expression extension due to security concerns

What it means

n8n sandbox-blocks a hardcoded set of unsafe property names (`__proto__`, `constructor`, `caller`, `arguments`, `__defineGetter__`/`__defineSetter__`, `getBuiltinModule`, `dlopen`, `execve`, `loadEnvFile`, ...) from being dispatched as expression extensions. This is a deliberate hardening to prevent prototype pollution and access to dangerous Node primitives from inside an expression.

Solutions

  1. Do not access these property names through expressions.
  2. Move the logic into a Code node where full (sandboxed) JS is available, and only return primitive values back into the workflow.
  3. Sanitize untrusted input before it reaches an expression.

Example fix

// before
{{ $json.foo.constructor('return process')() }}
// after
// move the computation into a Code node; do not reach through constructors in expressions
Defensive patterns

Strategy: validation

Validate before calling

const UNSAFE = new Set(['__proto__','constructor','prototype','caller','arguments','__defineGetter__','__defineSetter__','getBuiltinModule','dlopen','execve','loadEnvFile']);
const name = $json.methodName;
if (typeof name === 'string' && UNSAFE.has(name)) {
  throw new Error(`Refusing to dispatch blocked property: ${name}`);
}
return $json;

Type guard

const isSafePropertyName = (name: string): boolean =>
  !['__proto__','constructor','prototype','caller','arguments','__defineGetter__','__defineSetter__','getBuiltinModule','dlopen','execve','loadEnvFile'].includes(name);

Prevention

When it happens

Trigger: Invoking any blocked name as an extension method: `{{ $json.foo.constructor() }}`, `{{ $json.bar.__proto__ }}`, `{{ $json.x.dlopen() }}`, `{{ $json.y.loadEnvFile() }}`.

Common situations: Attempts at prototype-chain traversal in an expression; libraries/probes that walk `__proto__`; adversarial or untrusted input routed through an expression.

Related errors


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

Appendix: source

Thrown at packages/@n8n/expression-runtime/src/extensions/extend.ts:82

	'_linkedBinding',
	'_load',
	'prepareStackTrace',
	'__lookupGetter__',
	'__lookupSetter__',
	'__defineGetter__',
	'__defineSetter__',
	'caller',
	'arguments',
	'getBuiltinModule',
	'dlopen',
	'execve',
	'loadEnvFile',
]);

function findExtendedFunction(input: unknown, functionName: string): FoundFunction | undefined {
	const name = typeof functionName === 'string' ? functionName : String(functionName);
	if (UNSAFE_PROPERTY_NAMES.has(name)) {
		throw new ExpressionExtensionError(
			`Cannot access "${name}" via expression extension due to security concerns`,
		);
	}

	// eslint-disable-next-line @typescript-eslint/no-restricted-types
	let foundFunction: Function | undefined;
	if (Array.isArray(input)) {
		foundFunction = arrayExtensions.functions[name];
	} else if (isDate(input) && name !== 'toDate' && name !== 'toDateTime') {
		// If it's a string date (from $json), convert it to a Date object,
		// unless that function is `toDate`, since `toDate` does something
		// very different on date objects
		input = new Date(input as string);
		foundFunction = dateExtensions.functions[name];
	} else if (typeof input === 'string') {
		foundFunction = stringExtensions.functions[name];
	} else if (typeof input === 'number') {
		foundFunction = numberExtensions.functions[name];

View on GitHub (pinned to 5ac6606e81)