paperclipai/paperclip · error

Daytona sandbox lease ${scope.providerLeaseId} is no longer

Error message

Daytona sandbox lease ${scope.providerLeaseId} is no longer active.

What it means

Thrown by withSandboxActivityGate at loop entry when the lease admission state is closed (and allowClosed is not set). This is the first of three identical-throw sites in the gate loop; it blocks any new activity on a lease that is no longer active before checking teardown/activity gates.

Source

Thrown at packages/plugins/sandbox-providers/daytona/src/plugin.ts:1038

  function isClosed(scope: SandboxScope): boolean {
    return states.get(key(scope)) === true;
  }

  function reset(): void {
    states.clear();
  }

  return { open, close, isClosed, reset };
})();

async function withSandboxActivityGate<T>(
  scope: SandboxScope,
  fn: () => Promise<T>,
  options: SandboxLeaseAdmissionOptions = {},
): Promise<T> {
  while (true) {
    if (!options.allowClosed && sandboxHandleLeaseAdmissionStates.isClosed(scope)) {
      throw new Error(`Daytona sandbox lease ${scope.providerLeaseId} is no longer active.`);
    }

    const teardownGate = sandboxHandleTeardownGates.current(scope);
    if (teardownGate) {
      await teardownGate.promise;
      if (!options.allowClosed && sandboxHandleLeaseAdmissionStates.isClosed(scope)) {
        throw new Error(`Daytona sandbox lease ${scope.providerLeaseId} is no longer active.`);
      }
      continue;
    }

    const activityGate = await sandboxHandleActivityGates.begin(scope);
    try {
      // A teardown can still begin between the initial check above and the
      // activity-gate admission. If that happens, back out and wait for the
      // teardown to finish instead of proceeding into a race with cleanup.
      if (sandboxHandleTeardownGates.current(scope)) {
        continue;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Stop issuing operations against the lease after release/destroy; cancel queued tasks on teardown.
  2. If the operation must run, acquire a new lease and re-dispatch.
  3. Pass options.allowClosed only if the operation is intentionally tolerant of a closed lease (rare).
  4. Audit for code paths that retain a scope past lease release.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await withSandboxActivityGate(scope, () => op(), { allowClosed: false });
} catch (e) {
  if (e instanceof Error && e.message.includes('no longer active')) {
    // lease closed at entry; re-acquire or abort this task
  }
  throw e;
}

Prevention

When it happens

Trigger: Any sandbox operation routed through withSandboxActivityGate is attempted on a lease that sandboxHandleLeaseAdmissionStates marks closed, with options.allowClosed falsy. This site fires at the top of the while loop before any gate interaction.

Common situations: A concurrent teardown/destroy closed the lease while another caller still holds a reference and issues an operation; a lease was released upstream but a queued task still runs; retry logic re-entering after the lease closed during a wait.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/389baf2bdc9a1797. Report an issue: GitHub.