{"record":{"id":"22ef47d91f1f0c5e","repo":"Hmbown/CodeWhale","slug":"runtime-api-request-failed-status-message-22ef47","errorCode":null,"errorMessage":"Runtime API request failed (${status}): ${message}","messagePattern":"Runtime API request failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"integrations/telegram-bridge/src/index.mjs","lineNumber":614,"sourceCode":"    }\n  };\n  const typingTimer = setInterval(() => {\n    void tickTyping();\n  }, TYPING_INTERVAL_MS);\n  typingTimer.unref?.();\n\n  try {\n    void tickTyping();\n    const response = await fetch(\n      `${config.runtimeUrl}/v1/threads/${encodeURIComponent(threadId)}/events?since_seq=${sinceSeq}`,\n      {\n        headers: authHeaders(),\n        signal: controller.signal\n      }\n    );\n    if (!response.ok) {\n      const body = await readJsonSafe(response);\n      throw new Error(compactRuntimeError(response.status, body));\n    }\n\n    for await (const event of readSse(response)) {\n      if (!event.data) continue;\n      const record = JSON.parse(event.data);\n      latestSeq = Math.max(latestSeq, Number(record.seq || 0));\n      await flushLastSeq(false);\n\n      if (turnId && record.turn_id && record.turn_id !== turnId) continue;\n      const lifecycleStatus =\n        record.event === \"turn.lifecycle\"\n          ? record.payload?.turn?.status || record.payload?.status\n          : null;\n      const stopTypingEvent =\n        record.event === \"turn.completed\" ||\n        [\"failed\", \"canceled\", \"interrupted\"].includes(lifecycleStatus);\n      if (typingPaused && record.event !== \"approval.required\" && !stopTypingEvent) {\n        typingPaused = false;","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/integrations/telegram-bridge/src/index.mjs#L596-L632","documentation":"The telegram bridge's event pump fetches `${config.runtimeUrl}/v1/threads/{threadId}/events?since_seq=`; when response.ok is false, readJsonSafe parses the body and compactRuntimeError formats it as `Runtime API request failed (${status}): ${message}`, preferring body.error.message, then body.message, else the raw body. The status and embedded message say exactly what the Codewhale runtime rejected. The runtime URL comes from CODEWHALE_RUNTIME_URL/DEEPSEEK_RUNTIME_URL (default http://127.0.0.1:7878) and auth from CODEWHALE_RUNTIME_TOKEN/DEEPSEEK_RUNTIME_TOKEN.","triggerScenarios":"401 when the runtime token does not match; 404 when threadId no longer exists on the runtime; 5xx when the runtime behind CODEWHALE_RUNTIME_URL is unhealthy; a wrong port or stale URL in the env.","commonSituations":"Runtime token rotated in the TUI while the bridge keeps the old env value; runtime moved to a different port; idle chat's thread evicted by retention so the SSE resume 404s.","solutions":["Confirm the runtime is reachable at CODEWHALE_RUNTIME_URL from the bridge host (curl the /v1/threads endpoint with the same auth headers)","Re-sync the token: export CODEWHALE_RUNTIME_TOKEN to the current value the runtime accepts","GET /v1/threads/{threadId} to confirm the thread exists; recreate it if it was pruned","For 5xx, read the runtime's own message in the parentheses and check runtime logs — the bridge is only the messenger"],"exampleFix":"# before: runtime moved to port 7879\nexport CODEWHALE_RUNTIME_URL=http://127.0.0.1:7878\n# after\nexport CODEWHALE_RUNTIME_URL=http://127.0.0.1:7879","handlingStrategy":"retry","validationCode":"const probe = await fetch(\n  `${config.runtimeUrl}/v1/threads/${encodeURIComponent(threadId)}`,\n  { headers: authHeaders() }\n);\nif (!probe.ok) {\n  console.error(`runtime preflight failed: ${probe.status}`);\n  process.exit(2);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await streamThreadEvents(threadId, sinceSeq);\n} catch (error) {\n  const status = Number(error.message.match(/Runtime API request failed \\((\\d+)\\)/)?.[1]);\n  if (status >= 500) { await backoffAndRetry(); return; }\n  if (status === 401 || status === 404) { console.error('config drift — fix token/thread'); process.exit(2); }\n  throw error;\n}","preventionTips":["Preflight the runtime URL and token before entering the SSE loop","Treat 401/404 as config drift — alert and stop rather than retry","Log threadId with every failure so 404s trace back to pruned threads"],"tags":["network","api","runtime","sse","auth"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}