paperclipai/paperclip · error

Cloudflare sandbox bridge request timed out after ${requestT

Error message

Cloudflare sandbox bridge request timed out after ${requestTimeoutMs}ms.

What it means

Thrown by the Cloudflare bridge client's `requestJson` (bridge-client.ts:136-138) when the `AbortController` fires before the bridge responds. The request timeout is `resolveRequestTimeoutMs(config, path, init)`; for `/exec` it is the max of the configured `bridgeRequestTimeoutMs` and the body's requested `timeoutMs`, otherwise it is the configured default. The AbortError is converted into this plain Error.

Source

Thrown at packages/plugins/sandbox-providers/cloudflare/src/bridge-client.ts:136

    });
    const body = await parseJson(response);
    if (!response.ok) {
      const errorBody = isRecord(body) ? body as BridgeErrorBody : {};
      throw new CloudflareBridgeError({
        status: response.status,
        code: typeof errorBody.error === "string" ? errorBody.error : null,
        message:
          typeof errorBody.message === "string" && errorBody.message.trim().length > 0
            ? errorBody.message
            : `Cloudflare sandbox bridge request failed with HTTP ${response.status}.`,
        details: errorBody.details,
      });
    }
    return body as T;
  } catch (error) {
    if (error instanceof CloudflareBridgeError) throw error;
    if ((error as { name?: string } | null)?.name === "AbortError") {
      throw new Error(
        `Cloudflare sandbox bridge request timed out after ${requestTimeoutMs}ms.`,
      );
    }
    throw error;
  } finally {
    clearTimeout(timeout);
  }
}

async function requestResponse(
  config: CloudflareDriverConfig,
  path: string,
  init: RequestInit,
  extraHeaders: BridgeClientHeaders = {},
): Promise<Response> {
  const controller = new AbortController();
  const requestTimeoutMs = resolveRequestTimeoutMs(config, path, init);
  const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Raise `bridgeRequestTimeoutMs` in the driver config, or pass a larger `timeoutMs` in the exec body.
  2. For long-running commands, use the streaming exec path (supply `onOutput`) so the response is an SSE stream not bounded by the JSON request timeout.
  3. Retry transient network-induced timeouts.

Example fix

// before
await client.execute({ providerLeaseId, command: "npm run build", timeoutMs: 5000 });
// after
await client.execute({ providerLeaseId, command: "npm run build", timeoutMs: 60000 }, headers, { onOutput: console.log });
Defensive patterns

Strategy: retry

Validate before calling

// Raise the configured request timeout for long commands.
config.bridgeRequestTimeoutMs = Math.max(config.bridgeRequestTimeoutMs ?? 30000, 120000);

Type guard

function isBridgeTimeout(e: unknown): boolean {
  return e instanceof Error && /bridge request timed out/.test(e.message);
}

Try / catch

async function execWithRetry(client, body, headers) {
  for (let i = 0; i < 2; i++) {
    try {
      return await client.execute({ ...body, timeoutMs: 60000 }, headers);
    } catch (err) {
      if (isBridgeTimeout(err) && i === 0) continue;
      throw err;
    }
  }
}

Prevention

When it happens

Trigger: Any JSON bridge call takes longer than the resolved request timeout: a long-running non-streaming `/exec` command, a slow `/leases/acquire` cold start, or network latency/edge stalls between the driver and the bridge Worker.

Common situations: Running a synchronous command via `/exec` without `streamOutput` that legitimately runs long; bridge or Cloudflare edge congestion; or `bridgeRequestTimeoutMs` configured too low for the workload.

Understand the failure class

Related errors


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