mastra-ai/mastra · error · SandboxNotReadyError
SandboxNotReadyError: sandbox with id '${this.id}' is not re
Error message
SandboxNotReadyError: sandbox with id '${this.id}' is not ready What it means
ensureRunning() throws SandboxNotReadyError when the sandbox has already been destroyed — the instance can no longer execute commands or be restarted. It fails fast instead of attempting operations on a dead sandbox handle.
Source
Thrown at packages/core/src/workspace/sandbox/mastra-sandbox.ts:572
* where operations should automatically start the sandbox if needed.
*
* This is the lazy entry point into the id-keyed getOrCreate contract
* described on {@link start}.
*
* @throws {SandboxNotReadyError} if the sandbox fails to reach 'running' status
*
* @example
* ```typescript
* async executeCommand(command: string): Promise<CommandResult> {
* await this.ensureRunning();
* // Now safe to use the sandbox
* }
* ```
*/
async ensureRunning(): Promise<void> {
// Already destroyed — cannot use this sandbox
if (this.status === 'destroyed') {
throw new SandboxNotReadyError(this.id);
}
// During teardown the sandbox is still operational (e.g. destroy()
// may need to list/kill processes). Allow operations to proceed
// without trying to restart.
if (this.status === 'destroying' || this.status === 'stopping') {
return;
}
if (this.status !== 'running') {
await this._start();
}
if (this.status !== 'running') {
throw new SandboxNotReadyError(this.id);
}
}
/**
* Stop the sandbox (wrapper with status management and race-condition safety).
*View on GitHub (pinned to 75dd419e61)
Solutions
- Create a new sandbox instance instead of reusing the destroyed one.
- Check sandbox.status before use and recreate when it is 'destroyed'.
- Remove duplicate destroy() calls (e.g. both a timeout handler and afterEach) that kill the sandbox while it is still needed.
Example fix
// before
await sandbox.executeCommand('ls');
// after
if (sandbox.status === 'destroyed') {
sandbox = await createSandbox();
}
await sandbox.executeCommand('ls'); Defensive patterns
Strategy: validation
Validate before calling
if (sandbox.status === 'destroyed') {
sandbox = await createNewSandbox(); // recreate before use
} Type guard
function isUsable(sandbox: { status: string }): boolean {
return sandbox.status !== 'destroyed';
} Try / catch
try {
await sandbox.executeCommand(cmd);
} catch (e) {
if (e.name === 'SandboxNotReadyError' && sandbox.status === 'destroyed') {
sandbox = await createNewSandbox();
return sandbox.executeCommand(cmd);
}
throw e;
} Prevention
- Never cache sandbox instances across lifetimes; use a factory that recreates on demand.
- Check status before operations after any destroy()/stop() path.
- Ensure destroy() is called exactly once (single teardown owner) to avoid premature destruction.
- Clear references in tests' afterEach so late assertions can't use a destroyed sandbox.
When it happens
Trigger: Calling executeCommand()/ensureRunning() (or the `sandbox` helper) on a sandbox instance after destroy() completed, e.g. keeping a long-lived reference across a shutdown path or using a cached sandbox from a previous run.
Common situations: App code caching a sandbox in a module-level variable; a worker holding a sandbox reference that was destroyed by a timeout/cleanup handler; tests that destroy in afterEach but still use it in the last assertion.
Related errors
- Sandbox provider "${config.sandbox.provider}" does not suppo
- Cannot start a destroyed sandbox
- MastraAuthBetterAuth is not initialized — init() must run fi
- Shared browser not launched. Call createSharedSession() firs
- Browser not launched
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1b3e3b1c06189ba0.
Report an issue: GitHub.