Hmbown/CodeWhale · error · Error
Runtime API request failed (${status}): ${message}
Error message
Runtime API request failed (${status}): ${message} What it means
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.
Source
Thrown at integrations/feishu-bridge/src/index.mjs:342
async function streamTurnEvents(chatId, threadId, turnId, sinceSeq) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), config.turnTimeoutMs);
let responseText = "";
let latestSeq = sinceSeq;
let sentProgressAt = Date.now();
try {
const response = await fetch(
`${config.runtimeUrl}/v1/threads/${encodeURIComponent(threadId)}/events?since_seq=${sinceSeq}`,
{
headers: authHeaders(),
signal: controller.signal
}
);
if (!response.ok) {
const body = await readJsonSafe(response);
throw new Error(compactRuntimeError(response.status, body));
}
for await (const event of readSse(response)) {
if (!event.data) continue;
const record = JSON.parse(event.data);
latestSeq = Math.max(latestSeq, Number(record.seq || 0));
await threadStore.patchChat(chatId, { lastSeq: latestSeq });
if (turnId && record.turn_id && record.turn_id !== turnId) continue;
if (record.event === "item.delta" && record.payload?.kind === "agent_message") {
responseText += record.payload.delta || "";
const now = Date.now();
if (responseText.length > config.maxReplyChars && now - sentProgressAt > 15000) {
await sendText(chatId, responseText.slice(0, config.maxReplyChars));
responseText = responseText.slice(config.maxReplyChars);
sentProgressAt = now;
}View on GitHub (pinned to 8880682c63)
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
Example fix
// before
if (!response.ok) {
const body = await readJsonSafe(response);
throw new Error(compactRuntimeError(response.status, body));
}
// after - handle the polling-specific cases
if (response.status === 404) {
await threadStore.deleteChat(chatId);
return;
}
if (response.status >= 500) {
await sleep(1000);
return pollAgain();
}
if (!response.ok) {
const body = await readJsonSafe(response);
throw new Error(compactRuntimeError(response.status, body));
} Defensive patterns
Strategy: retry
Validate before calling
if (!(await threadExists(config, threadId)).exists) {
await threadStore.deleteChat(chatId);
return; // stop polling a deleted thread
} Try / catch
for (let attempt = 0; ; attempt++) {
try {
return await pollEvents(threadId, sinceSeq);
} catch (err) {
const m = /^Runtime API request failed \((\d+)\)/.exec(err.message);
if (m && m[1] === '404') { await threadStore.deleteChat(chatId); return; }
if (m && Number(m[1]) >= 500 && attempt < 5) { await sleep(2 ** attempt * 500); continue; }
throw err;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Bridge left running across a runtime restart with an old token; the user deletes the thread in the TUI; runtime under load returning 503.
Related errors
- Runtime API request failed (${status}): ${message}
- ${detail || ("HTTP " + res.status)}
- ${response.status} ${response.statusText}
- Runtime API request failed (${status}): ${message}
- Runtime API request failed (${status}): ${message}
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/0321402ac61c60ee.
Report an issue: GitHub.