{"record":{"id":"d877f752808f53ee","repo":"n8n-io/n8n","slug":"isolate-for-this-caller-is-no-longer-available","errorCode":null,"errorMessage":"Isolate for this caller is no longer available","messagePattern":"Isolate for this caller is no longer available","errorType":"exception","errorClass":"IsolateError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/expression-runtime/src/evaluator/expression-evaluator.ts","lineNumber":144,"sourceCode":"\t\t\t});\n\t\t\trecordOutcome(observability, start, 'success');\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\trecordOutcome(observability, start, 'error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate getBridge(caller: object): RuntimeBridge {\n\t\tconst bridge = this.bridgesByCaller.get(caller);\n\t\tif (!bridge) {\n\t\t\tthrow new IsolateError('No bridge acquired for this context. Call acquire() first.');\n\t\t}\n\n\t\t// If the isolate died mid-execution (e.g. OOM), all remaining expressions\n\t\t// in this execution are expected to fail. Recovery is per-execution, not per-expression.\n\t\tif (bridge.isDisposed()) {\n\t\t\tthrow new IsolateError('Isolate for this caller is no longer available');\n\t\t}\n\n\t\treturn bridge;\n\t}\n\n\tasync release(caller: object): Promise<void> {\n\t\tconst bridge = this.bridgesByCaller.get(caller);\n\t\tif (!bridge) return;\n\t\tthis.bridgesByCaller.delete(caller);\n\t\tawait this.pool.release(bridge);\n\t}\n\n\tasync waitForReplenishment(): Promise<void> {\n\t\tawait this.pool.waitForReplenishment();\n\t}\n\n\t/**\n\t * Transform a template expression to executable JavaScript via tournament.","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/expression-runtime/src/evaluator/expression-evaluator.ts#L126-L162","documentation":"Thrown as IsolateError('Isolate for this caller is no longer available') by ExpressionEvaluator.getBridge() when the caller's bridge reports bridge.isDisposed(). The most common cause is an OOM (MemoryLimitError, error 307) that killed the isolate mid-execution; once dead, ALL remaining expressions for that caller are expected to fail because recovery is per-execution, not per-expression. This is distinct from error 309 (whole evaluator disposed) — here only this caller's bridge is gone.","triggerScenarios":"After an expression on a caller triggers MemoryLimitError (or otherwise disposes the bridge), a subsequent evaluator.evaluate() call with the same caller object hits getBridge(), finds bridge.isDisposed() true, and throws. Also fired if the isolate was disposed externally while the caller still holds a reference.","commonSituations":"A workflow execution where expression #1 OOMs and expression #2 (same execution/caller) then fails with this error. A test that disposes a bridge manually but keeps calling evaluate(). A long execution that loses its isolate partway through.","solutions":["Treat this error as terminal for the execution: release the caller and fail/abort the execution rather than retrying in-place.","Ensure release(caller) is called in the execution's finally block so the dead bridge is returned/discarded.","Prevent the upstream OOM (see error 307) — reduce expression memory use.","If partial recovery is needed, acquire a fresh bridge for a new caller object instead of reusing the dead one."],"exampleFix":"// before — keep evaluating after the isolate died\nevaluator.evaluate(hugeExpr, data, caller); // OOM -> MemoryLimitError\nevaluator.evaluate(smallExpr, data, caller); // throws IsolateError\n// after — abandon the caller after OOM, release it\ntry {\n  evaluator.evaluate(hugeExpr, data, caller);\n} catch (e) {\n  if (e instanceof MemoryLimitError) {\n    await evaluator.release(caller); // drop the dead bridge\n    // do NOT reuse `caller`; fail the execution or start a new context\n  }\n}","handlingStrategy":"try-catch","validationCode":"// After any resource error, check the bridge before reusing the caller\nfunction callerIsAlive(evaluator, caller) {\n  // No public bridge getter; treat MemoryLimitError as fatal for the caller\n  return true; // see tryCatchPattern for the real guard pattern\n}","typeGuard":"import { IsolateError } from '@n8n/errors';\nfunction isCallerDead(e) { return e instanceof IsolateError && /no longer available/i.test(e.message); }","tryCatchPattern":"import { IsolateError, MemoryLimitError } from '@n8n/errors'; // MemoryLimitError from runtime types\ntry {\n  evaluator.evaluate(expr, data, caller);\n} catch (e) {\n  if (e instanceof IsolateError && /no longer available/i.test(e.message)) {\n    // this caller's isolate died (likely OOM); release and abandon the execution\n    await evaluator.release(caller);\n  } else throw e;\n}","preventionTips":["Treat a MemoryLimitError as fatal for that caller: release the bridge immediately.","Call evaluator.release(caller) in execution finally blocks so dead bridges are returned.","Do not reuse a caller object after its isolate died; acquire a fresh context.","Prevent upstream OOM (error 307) by bounding expression memory use."],"tags":["expression-runtime","isolate","oom-recovery","lifecycle","per-execution"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}