{"record":{"id":"a584e78ed56c3973","repo":"n8n-io/n8n","slug":"expression-timed-out-after-this-config-timeout-m","errorCode":null,"errorMessage":"Expression timed out after ${this.config.timeout}ms","messagePattern":"Expression timed out after (.+?)ms","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/expression-runtime/src/bridge/isolated-vm-bridge.ts","lineNumber":790,"sourceCode":"\n\t\t\tthis.logger.debug('[IsolatedVmBridge] Expression executed successfully');\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Re-throw reconstructed errors as-is.\n\t\t\t// Note: TypeError is intentionally NOT included here — the isolate's\n\t\t\t// E() handler swallows TypeErrors (failed attack attempts return undefined),\n\t\t\t// so TypeErrors from host callbacks should also go through the generic\n\t\t\t// wrapping for consistent behavior.\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\t(error.name === 'ExpressionError' || error.name === 'ExpressionExtensionError')\n\t\t\t) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMessage.includes('Script execution timed out')) {\n\t\t\t\tthrow new TimeoutError(`Expression timed out after ${this.config.timeout}ms`, {});\n\t\t\t}\n\t\t\tif (errorMessage.includes('memory limit')) {\n\t\t\t\tthrow new MemoryLimitError(\n\t\t\t\t\t`Expression exceeded memory limit of ${this.config.memoryLimit}MB`,\n\t\t\t\t\t{},\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(`Expression evaluation failed: ${errorMessage}`);\n\t\t} finally {\n\t\t\tgetValueAtPath.release();\n\t\t\tgetArrayElement.release();\n\t\t\tcallHost.release();\n\t\t}\n\t}\n\n\t/**\n\t * Reconstruct an error from serialized isolate data.\n\t *","sourceCodeStart":772,"sourceCodeEnd":808,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/expression-runtime/src/bridge/isolated-vm-bridge.ts#L772-L808","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — infinite loop in an expression\nconst result = evaluator.evaluate('while(true){}', data, caller);\n// after — bounded work, and guard the call\ntry {\n  const result = evaluator.evaluate('{{ $json.items.slice(0, 100).length }}', data, caller);\n} catch (e) {\n  if (e instanceof TimeoutError) {\n    // tell the author their expression is too slow / looping\n  }\n}","handlingStrategy":"try-catch","validationCode":"// You cannot fully prevent an arbitrary expression from looping, but you can bound work\n// before evaluation:\nfunction isBoundedExpression(expr) {\n  return !/\\bwhile\\s*\\(\\s*(?:true|1|!!1)\\s*\\)\\b/.test(expr) && !/\\bfor\\s*\\(\\s*;;\\s*\\)\\b/.test(expr);\n}","typeGuard":"import { TimeoutError } from '@n8n/expression-runtime';\nfunction isTimeoutError(e) { return e instanceof TimeoutError || e?.name === 'TimeoutError'; }","tryCatchPattern":"import { TimeoutError } from '@n8n/expression-runtime';\ntry {\n  const result = evaluator.evaluate(expr, data, caller);\n} catch (e) {\n  if (e instanceof TimeoutError) {\n    // expression ran > config.timeout ms (default 5000)\n    // refactor the expression or raise config.timeout\n  } else throw e;\n}","preventionTips":["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."],"tags":["expression-runtime","isolate","timeout","resource-limit"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}