{"record":{"id":"8b7fc8ac345be597","repo":"n8n-io/n8n","slug":"sandbox-this-name-failed-to-start-status","errorCode":null,"errorMessage":"Sandbox \"${this.name}\" failed to start (status: ${this.status})","messagePattern":"Sandbox \"(.+?)\" failed to start \\(status: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/workspace/sandbox/base-sandbox.ts","lineNumber":138,"sourceCode":"\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,\n\t\targs?: string[],\n\t\toptions?: ExecuteCommandOptions,\n\t): Promise<CommandResult> {\n\t\tawait this.ensureRunning({ abortSignal: options?.abortSignal });\n\t\tif (!this.processes) {\n\t\t\tthrow new Error(`Sandbox \"${this.name}\" has no process manager`);\n\t\t}\n\t\tconst fullCommand = args?.length ? `${command} ${args.map(shellQuote).join(' ')}` : command;\n\t\tconst handle = await this.processes.spawn(fullCommand, options);\n\t\treturn await raceWithAbort(\n\t\t\tasync () =>\n\t\t\t\tawait handle.wait({\n\t\t\t\t\tonStdout: options?.onStdout,","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/workspace/sandbox/base-sandbox.ts#L120-L156","documentation":"The post-start invariant in ensureRunning(): after awaiting raceWithAbort(_start), status must be 'running'. If it isn't, _start() returned without flipping status to 'running' (subclass bug), aborted, or landed in an error/pending state. The throw surfaces that the start silently no-op'd rather than letting executeCommand proceed against an unstarted sandbox.","triggerScenarios":"A subclass executeStart() resolved without setting status='running'; start was aborted mid-flight (raceWithAbort propagated abort, _start exited); start partially succeeded then hit an error path that left status as 'pending'/'error'.","commonSituations":"Custom BaseSandbox subclass whose executeStart() returns early on a recoverable condition without updating status; Daytona/E2B start aborted by an AbortSignal; provider returned success but the readiness probe was skipped.","solutions":["Ensure subclass executeStart() sets `this.status = 'ready'`/`'running'` only after the sandbox is truly ready, and throws on failure (so status stays non-running and this error correctly fires).","If aborted, check the AbortSignal — don't reuse the aborted sandbox; recreate and retry without the abort.","Inspect the status in the message to distinguish 'pending' (never started) from 'error'."],"exampleFix":"// before\nasync executeStart() {\n  await this.provider.create();\n  // forgot to set status\n}\n\n// after\nasync executeStart() {\n  await this.provider.create();\n  this.status = 'running';\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await sandbox.ensureRunning();\n} catch (e) {\n  if (e instanceof Error && /failed to start/.test(e.message)) {\n    // inspect sandbox.status: 'pending'/'error' → recreate; aborted → retry without the abort\n  } else throw e;\n}","preventionTips":["In subclass executeStart(), set status='running' only after true readiness and rethrow on failure.","Don't pass an already-aborted AbortSignal to ensureRunning().","Log status at the end of executeStart to catch silent no-ops."],"tags":["sandbox","lifecycle","state-machine","agents"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}