{"record":{"id":"04549541b5a085f1","repo":"n8n-io/n8n","slug":"sandbox-this-name-has-been-destroyed","errorCode":null,"errorMessage":"Sandbox \"${this.name}\" has been destroyed","messagePattern":"Sandbox \"(.+?)\" has been destroyed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/workspace/sandbox/base-sandbox.ts","lineNumber":125,"sourceCode":"\t/**\n\t * Drop the cached \"running\" state so the next `ensureRunning()`/`_start()`\n\t * re-runs the provider's `start()`. Used to recover when the remote sandbox\n\t * was stopped/deleted out from under us (the in-memory status is stale).\n\t * No-op once destroyed/destroying — those are terminal.\n\t *\n\t * Intentionally does NOT touch `startPromise`: clearing it would let a\n\t * concurrent caller bypass the single-flight start dedupe in `_start()` and\n\t * launch a second start. A start that is genuinely in flight will be awaited\n\t * (and deduped) by the next `_start()`.\n\t */\n\tprotected markNeedsStart(): void {\n\t\tif (this.status === 'destroyed' || this.status === 'destroying') return;\n\t\tthis.status = 'pending';\n\t}\n\n\tasync ensureRunning(options?: AbortableOptions): Promise<void> {\n\t\tif (this.status === 'destroyed') {\n\t\t\tthrow new Error(`Sandbox \"${this.name}\" has been destroyed`);\n\t\t}\n\t\tif (this.status === 'destroying') {\n\t\t\tif (this.destroyPromise) await this.destroyPromise.catch(() => {});\n\t\t\tthrow new Error(`Sandbox \"${this.name}\" has been destroyed`);\n\t\t}\n\t\tif (this.status === 'stopping') {\n\t\t\tif (this.stopPromise) await this.stopPromise.catch(() => {});\n\t\t}\n\t\tif (this.status !== 'running') {\n\t\t\tawait raceWithAbort(async () => await this._start(), options?.abortSignal);\n\t\t}\n\t\tif (this.status !== 'running') {\n\t\t\tthrow new Error(`Sandbox \"${this.name}\" failed to start (status: ${this.status})`);\n\t\t}\n\t}\n\n\tasync executeCommand(\n\t\tcommand: string,","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/workspace/sandbox/base-sandbox.ts#L107-L143","documentation":"BaseSandbox.ensureRunning() gates every command/process operation. When `status === 'destroyed'`, the sandbox has been fully torn down and cannot be reused, so it throws immediately rather than attempt a start that would fail confusingly deep inside _start(). This is the terminal-state branch of the sandbox state machine.","triggerScenarios":"Calling ensureRunning(), executeCommand(), or processes.spawn() on a sandbox instance after destroy() has completed — e.g. a reference held in a long-lived variable, a retry loop that didn't recreate the workspace, or a late timeout handler firing after cleanup.","commonSituations":"Agent code cached a sandbox reference across workspace lifecycles; a finally-block ran destroy() before an in-flight op resolved; an external timeout triggered cleanup while queued commands were still pending.","solutions":["Create a fresh sandbox/workspace — a destroyed sandbox is not recoverable.","Drop all references after destroy(); never reuse a sandbox object across workspace lifecycles.","Order teardown so it only runs after all in-flight commands have settled."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function isUsable(sandbox: { status: string }): boolean {\n  return sandbox.status !== 'destroyed' && sandbox.status !== 'destroying';\n}","typeGuard":null,"tryCatchPattern":"try {\n  await sandbox.ensureRunning();\n} catch (e) {\n  if (e instanceof Error && /has been destroyed/.test(e.message)) {\n    // create a new sandbox/workspace\n  } else throw e;\n}","preventionTips":["Never hold sandbox references across workspace lifecycles.","Await all in-flight commands before calling destroy().","Treat any 'destroyed' error as terminal — recreate, don't retry on the same object."],"tags":["sandbox","lifecycle","state-machine","agents"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}