musistudio/claude-code-router · error · Error

HTTP + response.status + from CCR remote sync

Error message

HTTP  + response.status +  from CCR remote sync

What it means

Thin fetch wrapper around the CCR remote-sync HTTP endpoint: any non-2xx status from fetch triggers this throw (with the status code in the message), after which the JSON body is never parsed. It signals the remote sync service rejected or failed the request.

Source

Thrown at packages/core/src/agents/codex/cli-middleware-runtime.ts:5078

        this.pollTimer = setTimeout(() => this.pollInbound(onInbound), numberEnv("CCR_REMOTE_SYNC_POLL_INTERVAL_MS", 2000));
      });
  }

  async request(method, suffix, body) {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), numberEnv("CCR_REMOTE_SYNC_REQUEST_TIMEOUT_MS", 5000));
    const headers = { "accept": "application/json" };
    if (body !== undefined) headers["content-type"] = "application/json";
    if (this.apiKey) headers.authorization = "Bearer " + this.apiKey;
    try {
      const response = await fetch(remoteSyncUrl(this.options.endpoint, suffix), {
        method,
        headers,
        body: body === undefined ? undefined : JSON.stringify(body),
        signal: controller.signal
      });
      if (!response.ok) {
        throw new Error("HTTP " + response.status + " from CCR remote sync");
      }
      return await response.json();
    } finally {
      clearTimeout(timeout);
    }
  }
}

async function readRemoteSyncApiKey() {
  const direct = nonEmptyEnv("CCR_REMOTE_SYNC_API_KEY");
  if (direct) return direct;
  const file = nonEmptyEnv("CCR_REMOTE_SYNC_API_KEY_FILE");
  if (file) {
    try {
      const content = fs.readFileSync(expandHome(file), "utf8");
      return String(content || "").split(/\r?\n/).map((line) => line.trim()).find(Boolean) || "";
    } catch (error) {
      log("remote_sync_api_key_file_failed", { error: formatError(error), file });

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Check the status code in the message: 401/403 → refresh credentials, 404 → verify endpoint URL/config, 5xx → retry later
  2. Print/inspect the response body (it is discarded before the throw) to get the server's error detail
  3. Verify the CCR base URL and auth headers in config against a working curl request
  4. Retry with backoff for transient 502/503/504 from gateways

Example fix

// before
if (!response.ok) throw new Error("HTTP " + response.status + " from CCR remote sync");

// after
if (!response.ok) {
  const detail = await response.text().catch(() => "");
  throw new Error(`HTTP ${response.status} from CCR remote sync: ${detail.slice(0, 200)}`);
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try { return await ccrRemoteSync(url, opts); } catch (e) { const m = /HTTP (\d+) from CCR/.exec(String(e)); if (m && ["502","503","504"].includes(m[1])) return await backoffRetry(() => ccrRemoteSync(url, opts)); throw e; }

Prevention

When it happens

Trigger: Any ccrRemoteSync call where response.ok is false: 401/403 auth failures, 404 wrong endpoint, 4xx bad payload, 5xx server errors, or a proxy returning an error page — all within the request's AbortController timeout.

Common situations: Expired or missing sync credentials; wrong CCR base URL configured; reverse proxy/gateway intercepting the request; CCR service outage or version mismatch changing routes; request body exceeding gateway limits (413).

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/1d6404ba215db149. Report an issue: GitHub.