n8n-io/n8n · error · ExpressionExtensionError
Cannot access "${name}" via expression extension due to secu
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.
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)
Solutions
- Do not access these property names through expressions.
- Move the logic into a Code node where full (sandboxed) JS is available, and only return primitive values back into the workflow.
- 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
- Never reach through `constructor` or `__proto__` in expressions.
- Do dynamic/reflective work in a Code node, returning only primitives.
- Treat untrusted input as untrusted — sanitize before it reaches an expression.
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
- ${label} "${candidate}" escapes ${root}
- Dangerous key "${dangerousKey}" found in JSON data
- Module '${moduleName}' is disallowed
- Security violation detected
- Security violations detected
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/1f4c1817d15e4bc7.
Report an issue: GitHub.