{"record":{"id":"a9d4283258d989c9","repo":"paperclipai/paperclip","slug":"the-stopped-daytona-sandbox-is-still-settling-cancelled-work","errorCode":null,"errorMessage":"The stopped Daytona sandbox is still settling cancelled work. Retry shortly.","messagePattern":"The stopped Daytona sandbox is still settling cancelled work\\. Retry shortly\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/plugins/sandbox-providers/daytona/src/plugin.ts","lineNumber":2221,"sourceCode":"      throw error;\n    }\n  },\n\n  async onEnvironmentResumeLease(\n    params: PluginEnvironmentResumeLeaseParams,\n  ): Promise<PluginEnvironmentLease> {\n    const config = parseDriverConfig(params.config);\n    const scope: SandboxScope = {\n      driverKey: params.driverKey,\n      companyId: params.companyId,\n      environmentId: params.environmentId,\n      providerLeaseId: params.providerLeaseId,\n      config,\n    };\n    // A confirmed stop may precede completion of an old SDK request. Do not\n    // restart its sandbox under that request; it could still write or execute.\n    if (sandboxHandleLeaseAdmissionStates.isClosed(scope) && sandboxHandleActivityGates.isActive(scope)) {\n      throw new Error(\"The stopped Daytona sandbox is still settling cancelled work. Retry shortly.\");\n    }\n    return await withSandboxActivityGate(scope, async () => {\n      const sandbox = await getSandboxOrNull(scope, { bypassTeardownGate: true });\n      if (!sandbox) {\n        return { providerLeaseId: null, metadata: { expired: true } };\n      }\n\n        // A stopped sandbox loses its session shell, so the stored session id is\n        // stale after a real restart. Clear the id only when the sandbox is not\n        // already running, and clear it before the restart. A stopped sandbox has\n        // no live session, so the clear drops a dead id and a later command opens\n        // a fresh session. A running sandbox keeps its live session, so the resume\n        // leaves the id in place; a concurrent command still finds it and teardown\n        // deletes one session. An unconditional clear would drop the id of a live\n        // session and leak its shell until sandbox reaping.\n        if (sandbox.state !== \"started\") {\n          sandboxHandleSessionStore.clear(scope);\n          // A stopped sandbox loses its pseudo-terminals, so a stored duplex channel","sourceCodeStart":2203,"sourceCodeEnd":2239,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/plugins/sandbox-providers/daytona/src/plugin.ts#L2203-L2239","documentation":"The Daytona sandbox provider throws this when an admission attempt (e.g. re-acquiring/restarting a lease) targets a sandbox that has been confirmed stopped but still has in-flight SDK activity from a previous request settling (cancelled work still draining). Restarting the sandbox under that old request could let it write or execute again, so the provider refuses and asks the caller to retry shortly.","triggerScenarios":"Calling lease admission/restart (the function building { providerLeaseId, config } around plugin.ts:2221) while sandboxHandleLeaseAdmissionStates.isClosed(scope) is true and sandboxHandleActivityGates.isActive(scope) is true — i.e. a stop was confirmed but an old SDK request is still active.","commonSituations":"A user cancels/stops work and immediately retries; a supervisor restarts a lease right after stop while a long-running exec still drains; race between stop confirmation and completion of an earlier SDK call.","solutions":["Wait briefly and retry the operation — the gate clears once the cancelled work finishes settling.","Add retry with backoff around lease admission when you recently stopped the sandbox.","Ensure your code awaits/cancels in-flight executions before confirming stop so the activity gate is idle before restarting.","If it persists, inspect the activity gate for a leaked active scope (unclosed activity registration) and file/fix the leak."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check gate state before admission if exposed:\n// if (states.isClosed(scope) && gates.isActive(scope)) await waitForSettle(scope);","typeGuard":null,"tryCatchPattern":"await retry(async () => admitLease(params), {\n  retries: 5,\n  factor: 2,\n  onRetry: (e) => { if (!/still settling cancelled work/.test(e.message)) throw e; }\n});","preventionTips":["Await or cancel in-flight executions before confirming a sandbox stop.","Add automatic retry with backoff after any stop operation.","Avoid immediately restarting a sandbox right after cancel; give it a settle window."],"tags":["sandbox","race-condition","retry","temporary"],"backgroundTag":"invalid-state-transition","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}