paperclipai/paperclip · error · CloudflareBridgeError
Cloudflare sandbox bridge request failed with HTTP ${respons
Error message
Cloudflare sandbox bridge request failed with HTTP ${response.status}. What it means
Thrown by the Cloudflare bridge client's `requestJson` (bridge-client.ts:122-128) as a `CloudflareBridgeError` when the bridge returns a non-2xx HTTP status and the JSON error body either has no `message` field or an empty/whitespace one. The literal `HTTP ${response.status}` is the fallback so the caller still sees the status code; a non-empty server `message` would override it.
Source
Thrown at packages/plugins/sandbox-providers/cloudflare/src/bridge-client.ts:122
path: string,
init: RequestInit,
extraHeaders: BridgeClientHeaders = {},
): Promise<T> {
const controller = new AbortController();
const requestTimeoutMs = resolveRequestTimeoutMs(config, path, init);
const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
const baseUrl = config.bridgeBaseUrl.replace(/\/+$/, "");
try {
const response = await fetch(`${baseUrl}${path}`, {
...init,
headers: buildHeaders(config, extraHeaders),
signal: controller.signal,
});
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;View on GitHub (pinned to 67001ec6eb)
Solutions
- Inspect the `CloudflareBridgeError.status` and `.code` fields (not just the message) to determine the real failure class.
- For 401, verify `config.bridgeAuthToken` matches the bridge's `BRIDGE_AUTH_TOKEN`.
- For 409 on resume, re-acquire the lease instead of resuming.
- For 400, validate the request body shape before sending.
Defensive patterns
Strategy: try-catch
Type guard
import { CloudflareBridgeError } from "@paperclipai/cloudflare-bridge-client";
function isBridgeError(e: unknown, status?: number): e is CloudflareBridgeError {
return e instanceof CloudflareBridgeError && (status === undefined || e.status === status);
} Try / catch
try {
await client.health();
} catch (err) {
if (err instanceof CloudflareBridgeError) {
if (err.status === 401) refreshBridgeToken();
else if (err.status === 400) fixRequestBody();
else throw err;
} else throw err;
} Prevention
- Always check CloudflareBridgeError.status and .code rather than the message string.
- Keep bridgeAuthToken aligned with the bridge's BRIDGE_AUTH_TOKEN.
- Validate request body shape (providerLeaseId, command) before sending.
When it happens
Trigger: Any JSON bridge call (`/health`, `/probe`, `/leases/*`, non-streaming `/exec`) gets a 4xx/5xx from the bridge Worker — e.g. 401 (bad bearer token), 400 `invalid_request`, 409 `sandbox_state_lost`, 404 route mismatch — and the response body omits a usable message string.
Common situations: Wrong/missing `bridgeAuthToken` (401), malformed request body (400), resuming a lease whose sandbox state was evicted (409), or a bridge deployment that returns plain status codes without a JSON message field.
Related errors
- Cloudflare sandbox bridge request timed out after ${requestT
- Cloudflare sandbox bridge streaming response had no body.
- Cloudflare sandbox bridge streaming response ended without a
- ${action} timed out: ${result.stderr.trim()}
- ${action} failed with exit code ${result.exitCode ?? "null"}
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/3cd814f7a4c9e839.
Report an issue: GitHub.