{"record":{"id":"94969a1a094d5f69","repo":"paperclipai/paperclip","slug":"createos-execution-requires-a-lease-from-this-environment","errorCode":null,"errorMessage":"CreateOS execution requires a lease from this environment.","messagePattern":"CreateOS execution requires a lease from this environment\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/sandbox-providers/createos/src/plugin.ts","lineNumber":80,"sourceCode":"\n  function key(params: PluginEnvironmentDriverBaseParams, id: string): string {\n    const config = parseConfig(params.config);\n    const account = createHash(\"sha256\").update(resolveApiKey(config)).digest(\"hex\");\n    return JSON.stringify([params.companyId, params.environmentId, config.apiUrl, account, id]);\n  }\n\n  async function stopActive(scope: string) {\n    const calls = [...(active.get(scope) ?? [])];\n    for (const call of calls) call.controller.abort();\n    await Promise.all(calls.map((call) => call.done));\n  }\n\n  async function track<T>(\n    params: PluginEnvironmentDriverBaseParams & { lease: PluginEnvironmentLease },\n    work: (client: CreateosClient, signal: AbortSignal) => Promise<T>,\n    timeoutOverride?: number,\n  ): Promise<T> {\n    if (!params.lease.providerLeaseId || !metadataMatches(params, params.lease.metadata)) throw new Error(\"CreateOS execution requires a lease from this environment.\");\n    const scope = key(params, params.lease.providerLeaseId);\n    if (shuttingDown || closing.has(scope) || unconfirmedCleanup.has(scope)) throw new Error(\"CreateOS lease is closing or requires cleanup.\");\n    const config = parseConfig(params.config);\n    const timeoutMs = timeoutOverride ?? config.timeoutMs;\n    if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86_400_000) throw new Error(\"Invalid CreateOS command timeout.\");\n    const controller = new AbortController();\n    let finish!: () => void;\n    const entry: Active = { controller, done: new Promise<void>((resolve) => { finish = resolve; }) };\n    const calls = active.get(scope) ?? new Set<Active>();\n    calls.add(entry);\n    active.set(scope, calls);\n    try {\n      return await work(new CreateosClient(config), AbortSignal.any([controller.signal, AbortSignal.timeout(timeoutMs)]));\n    } catch (error) {\n      if (error instanceof CreateosCleanupError) unconfirmedCleanup.add(scope);\n      throw error;\n    } finally {\n      calls.delete(entry);","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/plugins/sandbox-providers/createos/src/plugin.ts#L62-L98","documentation":"track() guards every sandbox operation (execute, sync in/out) with a lease-ownership check: the lease must have a providerLeaseId and its metadata must match the calling environment (provider 'createos', same companyId, environmentId, and apiUrl derived from the current config). This error is thrown when the lease passed to an operation was created by a different environment, company, or API URL, or has no providerLeaseId at all.","triggerScenarios":"onEnvironmentExecute (or sync) is called with params.lease whose metadata is undefined, whose metadata.provider !== 'createos', whose companyId/environmentId differ from params, or whose metadata.apiUrl differs from the apiUrl parsed from the current config; or params.lease.providerLeaseId is null/empty.","commonSituations":"Changing the CreateOS apiUrl (or shape/rootfs/region settings that feed apiUrl resolution) after a lease was acquired, so old lease metadata no longer matches config; passing a lease from environment A into an execute call for environment B; a host bug persisting/stripping lease metadata across process restarts; accidentally passing a non-CreateOS lease object.","solutions":["Release and re-acquire the lease under the current environment/config so its metadata is regenerated to match.","Confirm the lease's metadata fields (provider, companyId, environmentId, apiUrl) match the params used for the execute call.","If you changed apiUrl or connection config, drop stale persisted leases and re-probe the environment.","Check that the host is not swapping or truncating lease.metadata when deserializing persisted run state."],"exampleFix":"// before\nconst result = await driver.execute({ ...params, lease: leaseFromOtherEnv });\n// after: verify ownership before executing\nif (lease.metadata?.companyId !== params.companyId || lease.metadata?.environmentId !== params.environmentId) {\n  lease = await acquireLease(params); // re-acquire for this environment\n}\nconst result = await driver.execute({ ...params, lease });","handlingStrategy":"validation","validationCode":"function canUseLease(lease, params, apiUrl) {\n  const m = lease?.metadata;\n  return Boolean(lease?.providerLeaseId) && m?.provider === \"createos\" &&\n    m?.companyId === params.companyId && m?.environmentId === params.environmentId &&\n    m?.apiUrl === apiUrl;\n}","typeGuard":"function isUsableCreateosLease(l): l is PluginEnvironmentLease & { providerLeaseId: string } {\n  return typeof (l as any)?.providerLeaseId === \"string\" && (l as any).providerLeaseId.length > 0 &&\n    (l as any)?.metadata?.provider === \"createos\";\n}","tryCatchPattern":"try {\n  return await driver.execute({ ...params, lease });\n} catch (e) {\n  if (e.message.includes(\"requires a lease from this environment\")) {\n    lease = await driver.acquireLease(params); // re-acquire matching lease\n    return await driver.execute({ ...params, lease });\n  }\n  throw e;\n}","preventionTips":["Always propagate lease.metadata verbatim from acquire to subsequent calls.","Re-acquire leases whenever connection config (apiUrl) changes.","Never share lease objects across environments or companies.","Validate lease ownership before execute in the host layer with canUseLease()."],"tags":["lease","ownership","metadata-mismatch","sandbox"],"backgroundTag":"permission-denied","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"}