{"record":{"id":"80ed7a8994d84c14","repo":"paperclipai/paperclip","slug":"createos-workspace-requires-a-lease-from-this-environment","errorCode":null,"errorMessage":"CreateOS workspace requires a lease from this environment.","messagePattern":"CreateOS workspace requires a lease from this environment\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/sandbox-providers/createos/src/plugin.ts","lineNumber":183,"sourceCode":"        const sandbox = await client.getSandbox(params.providerLeaseId, signal);\n        if ([\"destroyed\", \"failed\"].includes(sandbox.status!)) return { providerLeaseId: null, metadata: { expired: true } };\n        await client.transition(params.providerLeaseId, \"running\", signal);\n        const data = await client.json(`/sandboxes/${params.providerLeaseId}/exec`, \"POST\", {\n          cmd: \"/bin/cat\", args: [MARKER],\n        }, signal);\n        const result = object(data.result);\n        if (result.exit_code !== 0 || result.stdout !== marker) return { providerLeaseId: null, metadata: { expired: true } };\n        return { providerLeaseId: params.providerLeaseId, metadata: { ...params.leaseMetadata, resumedLease: true } };\n      } catch (error) {\n        if (error instanceof CreateosApiError && error.status === 404) return { providerLeaseId: null, metadata: { expired: true } };\n        // A transient error does not prove the original sandbox is lost.\n        throw error;\n      }\n    },\n    onEnvironmentReleaseLease: (params) => release(params, false),\n    onEnvironmentDestroyLease: (params) => release(params, true),\n    async onEnvironmentRealizeWorkspace(params) {\n      if (!params.lease.providerLeaseId || !metadataMatches(params, params.lease.metadata)) throw new Error(\"CreateOS workspace requires a lease from this environment.\");\n      // The runtime's source mappings stage into this provider workspace.\n      return { cwd: CWD, metadata: { provider: \"createos\", remoteCwd: CWD } };\n    },\n    async onEnvironmentExecute(params: PluginEnvironmentExecuteParams) {\n      return track(params, (client, signal) => execute(client, params, signal,\n        (stream, text) => ctx?.execution.log(stream, text)), params.timeoutMs);\n    },\n    onEnvironmentSyncIn: (params) => track(params, (client, signal) => syncFiles(client, params, \"in\", signal)),\n    onEnvironmentSyncOut: (params) => track(params, (client, signal) => syncFiles(client, params, \"out\", signal)),\n    async onShutdown() {\n      shuttingDown = true;\n      await Promise.all([...active.keys()].map(stopActive));\n      ctx = null;\n    },\n  });\n}\n\nexport default createPlugin();","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/plugins/sandbox-providers/createos/src/plugin.ts#L165-L201","documentation":"The CreateOS sandbox provider plugin throws this when a workspace realization request arrives with a lease that does not belong to this environment: either the lease has no providerLeaseId or the lease metadata no longer matches the environment's recorded metadata. It exists to prevent realizing a workspace from a stale or foreign lease, which could stage runtime source mappings into the wrong sandbox context.","triggerScenarios":"Calling onEnvironmentRealizeWorkspace (via environment.workspace/realize paths) when params.lease.providerLeaseId is null/undefined, or when metadataMatches(params, params.lease.metadata) fails because the lease was created by a different environment or its metadata was mutated after lease creation.","commonSituations":"Reusing a lease object captured before an environment was recreated; a provider restart that rotated metadata; handing a lease from one environment to another; deserialized/cached lease state that lost its providerLeaseId.","solutions":["Re-acquire a fresh lease for this environment (onEnvironmentAcquireLease) and pass that lease to the realize call.","Verify the lease you pass still has a non-empty providerLeaseId; if not, the lease expired or failed activation and must be re-created.","Check that nothing mutated the lease.metadata since acquisition; restore the original metadata or re-acquire.","Confirm you are not crossing environments: realize the workspace with the lease issued by the same environment instance."],"exampleFix":"// before\nawait plugin.onEnvironmentRealizeWorkspace({ lease: staleLease });\n// after\nconst lease = await plugin.onEnvironmentAcquireLease(params);\nawait plugin.onEnvironmentRealizeWorkspace({ ...params, lease });","handlingStrategy":"validation","validationCode":"function canRealize(lease) {\n  return typeof lease?.providerLeaseId === 'string' && lease.providerLeaseId.length > 0 &&\n         lease.metadata != null;\n}\nif (!canRealize(lease)) lease = await acquireFreshLease(params);","typeGuard":"const isValidLease = (l) => typeof l?.providerLeaseId === 'string' && l.providerLeaseId.length > 0 && typeof l?.metadata === 'object';","tryCatchPattern":"try {\n  await plugin.onEnvironmentRealizeWorkspace({ lease });\n} catch (e) {\n  if (e.message.includes('requires a lease from this environment')) {\n    const fresh = await acquireLeaseForEnvironment(env);\n    await plugin.onEnvironmentRealizeWorkspace({ lease: fresh });\n  } else throw e;\n}","preventionTips":["Always pass the lease object obtained from the same environment instance you realize against.","Treat lease metadata as immutable after acquisition.","Re-acquire leases after environment restarts instead of reusing cached ones."],"tags":["sandbox","lease","workspace","invalid-state"],"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-22T11:17:16.035Z"}