paperclipai/paperclip · error

Environment driver " " does not support orphan sandbox…

Error message

Environment driver "${driver.driver}" does not support orphan sandbox teardown.

What it means

retryPendingSandboxTeardown delegates to the environment driver resolved from the lease. Not all drivers implement retryPendingSandboxTeardown (it is optional); when the resolved driver lacks it, the runtime throws 'Environment driver "X" does not support orphan sandbox teardown.'

Solutions

  1. Route the retry to the driver that originally owned the sandbox (fix getLeaseDriverKey/lease metadata)
  2. Upgrade or select a driver that supports orphan sandbox teardown
  3. Skip teardown-retry for drivers that don't support it — check driver.retryPendingSandboxTeardown before calling

Example fix

// before
await runtime.retryPendingSandboxTeardown({ environment, lease });
// after
const key = getLeaseDriverKey(lease, environment);
if (driverSupportsTeardown(key)) await runtime.retryPendingSandboxTeardown({ environment, lease });
else logger.warn({ leaseId: lease.id }, 'driver does not support orphan teardown');
Defensive patterns

Strategy: validation

Validate before calling

const key = getLeaseDriverKey(lease, environment);
const driver = getDriver(key);
if (!driver?.retryPendingSandboxTeardown) {
  console.warn(`Driver ${key} cannot retry orphan sandbox teardown; skipping`);
}

Type guard

function supportsTeardown(d: EnvironmentDriver): d is EnvironmentDriver & Required<Pick<EnvironmentDriver, "retryPendingSandboxTeardown">> {
  return typeof d.retryPendingSandboxTeardown === "function";
}

Try / catch

try {
  await runtime.retryPendingSandboxTeardown({ environment, lease });
} catch (e) {
  if (e instanceof Error && e.message.includes("does not support orphan sandbox teardown")) {
    // skip or manually clean the sandbox via the owning driver
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking orphan sandbox teardown retry for a lease whose driver (e.g., a driver without sandbox lifecycle support) has no retryPendingSandboxTeardown implementation.

Common situations: Switching an environment between drivers (e.g., local vs cloud) and then retrying teardown recorded under the old driver; admin/recovery tooling that calls teardown retry unconditionally for all leases.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/2c5e382d23ce819a. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/environment-runtime.ts:3839

      return released;
    },

    // Tear an orphan ephemeral sandbox down from its recorded provider config.
    // A failed acquire records the orphan as a `pending_cleanup` lease. The row
    // keeps the provider, the provider lease id, and the sandbox config, so this
    // teardown runs without the environment row and accepts a null environment.
    // The teardown resolves the recorded secret refs through the durable orphan
    // record, so a deleted or foreign-bound environment never strands it. The
    // caller owns the lease release; this dispatcher only runs the provider
    // teardown and throws when the driver teardown throws.
    async retryPendingSandboxTeardown(input: {
      environment: Environment | null;
      lease: EnvironmentLease;
    }): Promise<unknown> {
      const driver = requireDriverKey(getLeaseDriverKey(input.lease, input.environment));
      if (!driver.retryPendingSandboxTeardown) {
        throw new Error(
          `Environment driver "${driver.driver}" does not support orphan sandbox teardown.`,
        );
      }
      return await driver.retryPendingSandboxTeardown(input);
    },

    // Report whether the provider worker can run an orphan teardown now. The
    // cleanup sweep calls this before it claims a finite retry attempt, so a
    // briefly-down plugin worker never burns an attempt. This dispatcher never
    // throws: an unregistered driver, or a driver with no probe, reports ready,
    // so the teardown still runs and its own failure counts toward the cap.
    async isPendingCleanupWorkerReady(input: {
      environment: Environment | null;
      lease: EnvironmentLease;
    }): Promise<boolean> {
      const driver = getDriver(getLeaseDriverKey(input.lease, input.environment));
      if (!driver?.isPendingCleanupWorkerReady) return true;
      return driver.isPendingCleanupWorkerReady(input);

View on GitHub (pinned to 3f1d897a7c)