paperclipai/paperclip · error · Error

Timed out checking command "${command}" on sandbox target.

Error message

Timed out checking command "${command}" on sandbox target.

What it means

Thrown by ensureSandboxCommandResolvable when the initial `command -v <cmd>` probe inside a sandbox execution target does not return within the configured timeout (target.timeoutMs or 15s default). The sandbox runner wraps the probe in `sh -c`, so a timeout means the sandbox lease is unresponsive, network-stalled, or the shell environment is broken. This fires BEFORE any install command is attempted.

Source

Thrown at packages/adapter-utils/src/execution-target.ts:522

}

async function ensureSandboxCommandResolvable(
  command: string,
  target: AdapterSandboxExecutionTarget,
  installCommand: string | null,
  timeoutSec?: number | null,
): Promise<void> {
  // Probe whether the binary is resolvable inside the sandbox. We previously
  // short-circuited this for sandbox targets, which let the caller report a
  // success message even when the CLI was missing from the image. Now we run
  // a real `command -v` through the same runner the hello probe will use, so
  // the first step honestly reflects whether the binary is on PATH. The
  // sandbox provider is responsible for sourcing login profiles (e2b mirrors
  // SSH's buildSshSpawnTarget) so this and the hello probe agree on PATH.
  let probe = await probeSandboxCommandResolvable(command, target);
  if (probe.resolved) return;
  if (probe.timedOut) {
    throw new Error(`Timed out checking command "${command}" on sandbox target.`);
  }

  // If the caller supplied an install command, attempt the install once via
  // the sandbox runner (which the sandbox provider wraps in a login shell)
  // and re-probe before reporting failure. This lets fresh sandbox leases
  // bring up the CLI before the resolvability gate, mirroring the test path.
  let installFailureDetail: string | null = null;
  if (installCommand) {
    const runner = requireSandboxRunner(target);
    const installTimeoutMs =
      typeof timeoutSec === "number" && Number.isFinite(timeoutSec) && timeoutSec > 0
        ? Math.floor(timeoutSec * 1000)
        : target.timeoutMs ?? 300_000;
    try {
      const installResult = await runner.execute({
        command: "sh",
        args: shellCommandArgs(installCommand),
        cwd: target.remoteCwd,

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Increase target.timeoutMs (or the caller's timeoutSec) to give the cold sandbox lease more time to respond.
  2. Verify the sandbox lease is healthy — check provider dashboard/logs for the lease ID and confirm the VM is running and reachable.
  3. Ensure the sandbox provider sources login profiles correctly so the shell does not hang waiting on interactive profile prompts.
  4. Retry the operation after recycling the sandbox lease if the provider reports the lease as stale.

Example fix

// before
const target: AdapterSandboxExecutionTarget = {
  kind: "remote",
  transport: "sandbox",
  remoteCwd: "/workspace",
  timeoutMs: 5_000, // too short for cold start
};
// after
const target: AdapterSandboxExecutionTarget = {
  kind: "remote",
  transport: "sandbox",
  remoteCwd: "/workspace",
  timeoutMs: 30_000, // allow time for lease warm-up
};
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check sandbox lease health before probing commands
async function isSandboxResponsive(target: AdapterSandboxExecutionTarget): Promise<boolean> {
  const runner = requireSandboxRunner(target);
  try {
    const result = await runner.execute({
      command: "sh",
      args: ["-c", "echo ok"],
      cwd: target.remoteCwd,
      timeoutMs: Math.min(target.timeoutMs ?? 15_000, 10_000),
    });
    return !result.timedOut && (result.exitCode ?? 1) === 0;
  } catch {
    return false;
  }
}

Try / catch

try {
  await ensureSandboxCommandResolvable(command, target, installCommand, timeoutSec);
} catch (err) {
  if (err instanceof Error && err.message.includes("Timed out checking command")) {
    // Sandbox may still be warming up — recycle lease and retry once
    await recycleSandboxLease(target);
    await ensureSandboxCommandResolvable(command, target, installCommand, timeoutSec);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling ensureSandboxCommandResolvable(command, target, installCommand, timeoutSec) where target.kind === 'remote' && target.transport === 'sandbox'. The probeSandboxCommandResolvable helper runs `sh -c 'command -v <cmd>'` via the sandbox runner's execute() with timeoutMs = target.timeoutMs ?? 15_000; that call returns { timedOut: true }.

Common situations: Sandbox provider (e2b or similar) lease is still warming up or has died; sandbox network proxy is blocking the exec channel; the sandbox image has a broken login shell that hangs on startup; target.timeoutMs is set too low for a cold lease; the sandbox host is under heavy load.

Understand the failure class

Related errors


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