n8n-io/n8n · error · TimeoutError
Expression timed out after ${this.config.timeout}ms
Error message
Expression timed out after ${this.config.timeout}ms What it means
Thrown as TimeoutError by IsolatedVmBridge.execute() when the V8 isolate aborts the script with a message containing 'Script execution timed out'. The isolate is given a per-evaluation timeout (config.timeout, default 5000ms via DEFAULT_BRIDGE_CONFIG); exceeding it terminates the script. TimeoutError extends ExpressionError and is the classified 'timeout' bucket in observability. The isolate itself survives (unlike OOM), but the offending expression is aborted.
Source
Thrown at packages/@n8n/expression-runtime/src/bridge/isolated-vm-bridge.ts:790
this.logger.debug('[IsolatedVmBridge] Expression executed successfully');
return result;
} catch (error) {
// Re-throw reconstructed errors as-is.
// Note: TypeError is intentionally NOT included here — the isolate's
// E() handler swallows TypeErrors (failed attack attempts return undefined),
// so TypeErrors from host callbacks should also go through the generic
// wrapping for consistent behavior.
if (
error instanceof Error &&
(error.name === 'ExpressionError' || error.name === 'ExpressionExtensionError')
) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage.includes('Script execution timed out')) {
throw new TimeoutError(`Expression timed out after ${this.config.timeout}ms`, {});
}
if (errorMessage.includes('memory limit')) {
throw new MemoryLimitError(
`Expression exceeded memory limit of ${this.config.memoryLimit}MB`,
{},
);
}
throw new Error(`Expression evaluation failed: ${errorMessage}`);
} finally {
getValueAtPath.release();
getArrayElement.release();
callHost.release();
}
}
/**
* Reconstruct an error from serialized isolate data.
*View on GitHub (pinned to 5ac6606e81)
Solutions
- Catch TimeoutError specifically and surface a clear message to the workflow author.
- Raise config.timeout if the expression legitimately needs more time (weigh against DoS risk).
- Refactor the expression to avoid loops / catastrophic regex; move heavy work into a Code node.
- If using the evaluator, the error is classified as 'timeout' in metrics — check observability for recurring offenders.
Example fix
// before — infinite loop in an expression
const result = evaluator.evaluate('while(true){}', data, caller);
// after — bounded work, and guard the call
try {
const result = evaluator.evaluate('{{ $json.items.slice(0, 100).length }}', data, caller);
} catch (e) {
if (e instanceof TimeoutError) {
// tell the author their expression is too slow / looping
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// You cannot fully prevent an arbitrary expression from looping, but you can bound work
// before evaluation:
function isBoundedExpression(expr) {
return !/\bwhile\s*\(\s*(?:true|1|!!1)\s*\)\b/.test(expr) && !/\bfor\s*\(\s*;;\s*\)\b/.test(expr);
} Type guard
import { TimeoutError } from '@n8n/expression-runtime';
function isTimeoutError(e) { return e instanceof TimeoutError || e?.name === 'TimeoutError'; } Try / catch
import { TimeoutError } from '@n8n/expression-runtime';
try {
const result = evaluator.evaluate(expr, data, caller);
} catch (e) {
if (e instanceof TimeoutError) {
// expression ran > config.timeout ms (default 5000)
// refactor the expression or raise config.timeout
} else throw e;
} Prevention
- Avoid while(true) and unbounded loops in expressions; move heavy logic to a Code node.
- Set config.timeout deliberately (default 5000ms) — high enough for legitimate work, low enough to bound DoS.
- Watch for catastrophic-backtracking regexes inside expressions.
- Monitor the 'timeout' observability bucket to find slow expressions.
When it happens
Trigger: An n8n expression evaluated in the isolate runs longer than config.timeout: an infinite or near-infinite loop (while(true){}), a very expensive computation over a large array, or a deeply recursive user expression. Reproduced in tests with bridge.execute('while(true){}', {}) at timeout:100.
Common situations: A user-authored expression with a bug causing an infinite loop. Processing a huge array with a naive O(n^2) expression. A regex with catastrophic backtracking inside an expression. timeout configured too low for legitimate heavy expressions.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Expression exceeded memory limit of ${this.config.memoryLimi
- Expression evaluation failed: ${errorMessage}
- Isolate for this caller is no longer available
- Database connection timed out
- Timed out after ${timeoutMs}ms waiting for database connecti
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/a584e78ed56c3973.
Report an issue: GitHub.