{"record":{"id":"7d1bc7314694b0ba","repo":"mastra-ai/mastra","slug":"cannot-start-a-destroyed-sandbox","errorCode":null,"errorMessage":"Cannot start a destroyed sandbox","messagePattern":"Cannot start a destroyed sandbox","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/workspace/sandbox/mastra-sandbox.ts","lineNumber":405,"sourceCode":"   * Subclasses override `start()` to provide their startup logic.\n   */\n  async _start(): Promise<SandboxStartResult | void> {\n    // Already running — definitionally not a fresh create. Reporting\n    // 'connected' (rather than nothing) keeps every path through the wrapper\n    // result-bearing for providers whose `start()` always reports one.\n    if (this.status === 'running') {\n      return { outcome: 'connected' };\n    }\n\n    // Wait for in-flight stop/destroy before starting.\n    // Intentionally no .catch() — if teardown is failing, _start() should propagate\n    // that error rather than silently starting on top of a broken state.\n    if (this._stopPromise) await this._stopPromise;\n    if (this._destroyPromise) await this._destroyPromise;\n\n    // Cannot start a destroyed sandbox\n    if (this.status === 'destroyed') {\n      throw new Error('Cannot start a destroyed sandbox');\n    }\n\n    // Start already in progress — join it and share its result. The slot is\n    // cleared on settle, so a failed attempt is never latched.\n    if (this._startPromise) {\n      return this._startPromise;\n    }\n\n    // Create and store the start promise\n    this._startPromise = this._executeStart();\n\n    try {\n      return await this._startPromise;\n    } finally {\n      this._startPromise = undefined;\n    }\n  }\n","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workspace/sandbox/mastra-sandbox.ts#L387-L423","documentation":"MastraSandbox._start() throws if the sandbox's lifecycle status is 'destroyed'. A destroyed sandbox has released its resources permanently and cannot be restarted; starting over it would run on top of broken state. The method also awaits any in-flight stop/destroy promises first so a concurrent destroy is honored before the check.","triggerScenarios":"Calling `start()`/`ensureRunning()` (directly or via internal helpers) after `destroy()` or `dispose()` has completed on the same sandbox instance; holding a stale reference to a sandbox that was destroyed elsewhere.","commonSituations":"Long-lived caches or DI containers holding sandbox instances across lifecycles; retry logic that calls start() on a handle whose destroy already ran; hot-reload in dev servers reusing old sandbox objects.","solutions":["Create a new sandbox instance instead of restarting the destroyed one","Check `sandbox.status !== 'destroyed'` before calling start(), and recreate when it is destroyed","Fix ownership: ensure only one code path can destroy the sandbox and refresh references after destroy"],"exampleFix":"// before\nawait sandbox.destroy();\nawait sandbox.start(); // throws\n// after\nawait sandbox.destroy();\nsandbox = new LocalSandbox(options);\nawait sandbox.start();","handlingStrategy":"validation","validationCode":"if (sandbox.status === 'destroyed') {\n  sandbox = createSandbox(options);\n}\nawait sandbox.start();","typeGuard":"function isRestartable(s: { status: string }): s is { status: Exclude<string, 'destroyed'> } {\n  return s.status !== 'destroyed';\n}","tryCatchPattern":"try {\n  await sandbox.start();\n} catch (err) {\n  if (/Cannot start a destroyed sandbox/.test(String(err?.message))) {\n    sandbox = new LocalSandbox(options);\n    await sandbox.start();\n  } else throw err;\n}","preventionTips":["Null out or replace references after destroy() so stale handles can't be reused","Centralize sandbox lifecycle (create/start/destroy) in one owner module","In retry loops, recreate the sandbox rather than re-calling start() on the same instance"],"tags":["lifecycle","sandbox","state","destroyed"],"backgroundTag":"use-after-destroy","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}