paperclipai/paperclip · error
CreateOS sandbox did not reach
Error message
CreateOS sandbox did not reach ${desired}. What it means
transition submits a lifecycle action (pause, resume, etc.) to CreateOS and then polls until the sandbox reaches the desired state within a deadline. This error is thrown when the sandbox cannot be transitioned (no action was submittable) and its current status is not one of the transient in-flight states (creating/pausing/resuming), so it will never reach the desired state on its own.
Solutions
- Fetch the sandbox's current status via getSandbox and inspect why it is not in a transitional state before retrying.
- If the sandbox is in a terminal/failed state, create a new sandbox instead of reusing this one.
- Re-run the transition after confirming no concurrent operator action is holding the sandbox; transient states resolve within the poll deadline.
- Check CreateOS dashboard/API for quota, region capacity, or account issues that block state transitions.
Example fix
// before: blind retry loops forever or throws
await provider.release(sandbox);
// after: check status and fall back to creating a fresh sandbox
try {
await provider.release(sandbox);
} catch (err) {
if (err.message.includes("did not reach")) {
const current = await provider.getSandbox(sandbox.id);
if (["failed", "terminated"].includes(current.status)) sandbox = await provider.createSandbox(signal);
}
} Defensive patterns
Strategy: retry
Validate before calling
const TRANSITIONAL = ["creating", "pausing", "resuming"];
const TERMINAL = ["failed", "terminated", "deleted"];
async function canTransition(provider, sandbox, desired) {
const current = await provider.getSandbox(sandbox.id);
return !TERMINAL.includes(current.status);
} Type guard
function isTransitionable(status: string): boolean {
return !["failed", "terminated", "deleted"].includes(status);
} Try / catch
try {
await provider.release(sandbox, signal);
} catch (err) {
if (err.message.includes("did not reach")) {
const { status } = await provider.getSandbox(sandbox.id);
if (isTransitionable(status)) throw err; // retry later
return recreateSandbox(); // terminal: replace it
}
throw err;
} Prevention
- Check sandbox status before issuing pause/resume transitions.
- Treat 'did not reach' as a signal to inspect current status, not to retry blindly.
- Recreate sandboxes that reach terminal/failed states instead of reusing them.
- Keep lease operations within the provider's poll deadline window.
When it happens
Trigger: Calling acquire/release/onEnvironmentResumeLease when the sandbox status is a terminal or incompatible state (e.g. "failed", "terminated", "paused" while resuming was never accepted, or a 409 was never returned because the provider rejects the action outright without a conflict).
Common situations: Sandbox previously failed provisioning and sits in a dead status; a CreateOS-side operation cleaned up or terminated the sandbox; an operator manually paused/destroyed the sandbox; or polling deadline logic races a state that skipped through the expected intermediate states.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- CreateOS command cleanup failed; process termination is…
- CreateOS command probe failed.
- CreateOS transfer command failed.
- Failed to stop Daytona sandbox during lease release
- The pause was saved, but stopping could not be verified…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/2deca77ef3af053d.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/client.ts:126
async transition(id: string, desired: "running" | "paused", signal: AbortSignal): Promise<void> {
let submitted = false;
for (;;) {
signal.throwIfAborted();
const sandbox = await this.getSandbox(id, signal);
if (sandbox.status === desired) return;
const canSubmit = desired === "running"
? ["paused", "error"].includes(sandbox.status!)
: sandbox.status === "running";
if (canSubmit && !submitted) {
try {
await this.json(`/sandboxes/${id}/${desired === "running" ? "resume" : "pause"}`, "POST", undefined, signal);
submitted = true;
} catch (error) {
// A concurrent state transition is reconciled by reading its state.
if (!(error instanceof CreateosApiError && error.status === 409)) throw error;
}
} else if (!canSubmit && !["creating", "pausing", "resuming"].includes(sandbox.status!)) {
throw new Error(`CreateOS sandbox did not reach ${desired}.`);
}
// An accepted transition can remain in its previous state briefly.
// Poll under the same deadline without submitting the action twice.
await delay(250, undefined, { signal });
}
}
async upload(id: string, path: string, content: string, signal: AbortSignal): Promise<void> {
const response = await this.request(`/sandboxes/${identifier(id)}/files?path=${encodeURIComponent(path)}`, {
method: "PUT", headers: { "Content-Type": "application/octet-stream" }, body: content, signal,
});
await response.body?.cancel();
}
}
View on GitHub (pinned to 3f1d897a7c)