Hmbown/CodeWhale · error · Error

Runtime API request failed (${status}): ${message}

Error message

Runtime API request failed (${status}): ${message}

What it means

runtimeJson is the weixin-bridge's shared helper for every runtime call: it builds `${config.runtimeUrl}${subPath}`, optionally JSON-stringifies the body, and throws `Runtime API request failed (${status}): ${compactRuntimeError...}` whenever response.ok is false. Because all runtime GET/POSTs funnel through it, the failing endpoint is identified by the subPath used at the call site.

Source

Thrown at integrations/weixin-bridge/src/index.mjs:208

  };
}

async function readJsonSafe(response) {
  try {
    return await response.json();
  } catch {
    return null;
  }
}

async function runtimeJson(subPath, { method = "GET", body = null, auth = true } = {}) {
  const url = `${config.runtimeUrl}${subPath}`;
  const options = { method, headers: auth ? authHeaders() : {} };
  if (body) options.body = JSON.stringify(body);
  const response = await fetch(url, options);
  const result = await readJsonSafe(response);
  if (!response.ok) {
    throw new Error(compactRuntimeError(response.status, result));
  }
  return result;
}

async function* readSse(response) {
  let buffer = "";
  for await (const chunk of response.body) {
    buffer += new TextDecoder().decode(chunk, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() || "";
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed) continue;
      if (trimmed.startsWith("data:")) {
        yield { data: trimmed.slice(5).trim() };
      } else if (trimmed.startsWith("event:")) {
        yield { event: trimmed.slice(6).trim() };
      } else if (trimmed.startsWith("id:")) {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Reproduce with curl against `${config.runtimeUrl}${subPath}` using the same auth headers to see the raw status and body
  2. Align the runtime URL and token env values with what the runtime currently accepts, then retry
  3. For 400s, log the request body — the runtime's message names the invalid field
  4. For 5xx, inspect runtime logs; the bridge is a thin client and cannot fix server-side failures

Example fix

# before: 401 from a token issued by an older runtime
CODEWHALE_RUNTIME_TOKEN=old node src/index.mjs
# after
CODEWHALE_RUNTIME_TOKEN=current node src/index.mjs
Defensive patterns

Strategy: retry

Validate before calling

const probe = await fetch(config.runtimeUrl, { headers: authHeaders() });
if (!probe.ok && probe.status >= 500) {
  console.error('runtime unhealthy — delaying bridge work');
}

Try / catch

try {
  await runtimeJson(subPath, { method, body });
} catch (error) {
  const status = Number(error.message.match(/Runtime API request failed \((\d+)\)/)?.[1]);
  if (status >= 500 || status === 429) { await delay(1000); return runtimeJson(subPath, { method, body }); }
  throw error;
}

Prevention

When it happens

Trigger: Any /v1/... request returning non-OK: 401 for a bad runtime token, 404 for unknown thread/turn ids in the subPath, 400 for a malformed request body, 5xx when the runtime itself crashes.

Common situations: Weixin bridge configured with a stale runtimeUrl; auth token rejected after rotation; passing ids the runtime pruned (old turns or threads).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/86778e07c9a7732a. Report an issue: GitHub.