{"record":{"id":"0321402ac61c60ee","repo":"Hmbown/CodeWhale","slug":"runtime-api-request-failed-status-message-032140","errorCode":null,"errorMessage":"Runtime API request failed (${status}): ${message}","messagePattern":"Runtime API request failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"integrations/feishu-bridge/src/index.mjs","lineNumber":342,"sourceCode":"\nasync function streamTurnEvents(chatId, threadId, turnId, sinceSeq) {\n  const controller = new AbortController();\n  const timeout = setTimeout(() => controller.abort(), config.turnTimeoutMs);\n  let responseText = \"\";\n  let latestSeq = sinceSeq;\n  let sentProgressAt = Date.now();\n\n  try {\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 threadStore.patchChat(chatId, { lastSeq: latestSeq });\n\n      if (turnId && record.turn_id && record.turn_id !== turnId) continue;\n\n      if (record.event === \"item.delta\" && record.payload?.kind === \"agent_message\") {\n        responseText += record.payload.delta || \"\";\n        const now = Date.now();\n        if (responseText.length > config.maxReplyChars && now - sentProgressAt > 15000) {\n          await sendText(chatId, responseText.slice(0, config.maxReplyChars));\n          responseText = responseText.slice(config.maxReplyChars);\n          sentProgressAt = now;\n        }","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/integrations/feishu-bridge/src/index.mjs#L324-L360","documentation":"The Feishu bridge's event pump long-polls GET /v1/threads/<id>/events?since_seq=N; any non-2xx on that stream is wrapped by compactRuntimeError with the runtime's own message. Because this is a polling loop, the error recurs until the underlying condition (auth, thread existence, runtime health) is fixed.","triggerScenarios":"401 from stale auth headers after the runtime restarted; 404 when the tracked thread was deleted server-side; 5xx while the runtime shuts down or crash-loops mid-poll.","commonSituations":"Bridge left running across a runtime restart with an old token; the user deletes the thread in the TUI; runtime under load returning 503.","solutions":["Recreate the runtime session, update the bridge's token env, restart the bridge","On 404, drop or reset the stored chat mapping (threadStore) so polling stops","Add backoff/reconnect for transient 5xx instead of failing the handler"],"exampleFix":"// before\nif (!response.ok) {\n  const body = await readJsonSafe(response);\n  throw new Error(compactRuntimeError(response.status, body));\n}\n\n// after - handle the polling-specific cases\nif (response.status === 404) {\n  await threadStore.deleteChat(chatId);\n  return;\n}\nif (response.status >= 500) {\n  await sleep(1000);\n  return pollAgain();\n}\nif (!response.ok) {\n  const body = await readJsonSafe(response);\n  throw new Error(compactRuntimeError(response.status, body));\n}","handlingStrategy":"retry","validationCode":"if (!(await threadExists(config, threadId)).exists) {\n  await threadStore.deleteChat(chatId);\n  return; // stop polling a deleted thread\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; ; attempt++) {\n  try {\n    return await pollEvents(threadId, sinceSeq);\n  } catch (err) {\n    const m = /^Runtime API request failed \\((\\d+)\\)/.exec(err.message);\n    if (m && m[1] === '404') { await threadStore.deleteChat(chatId); return; }\n    if (m && Number(m[1]) >= 500 && attempt < 5) { await sleep(2 ** attempt * 500); continue; }\n    throw err;\n  }\n}","preventionTips":["Refresh the runtime token whenever the runtime restarts","Drop chat mappings on 404 instead of retrying forever","Cap retry attempts and alert when the poll loop keeps failing"],"tags":["feishu","bridge","http","sse","runtime"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}