Hmbown/CodeWhale · error · RuntimeApiError
Runtime API event response did not include a readable body
Error message
Runtime API event response did not include a readable body
What it means
fleetEvents yields events from the runtime API's event stream. When the API has no buffered events it expects to consume an SSE stream from response.body; if the response has no readable body it throws RuntimeApiError, since streaming is impossible.
Solutions
- If using a fetch mock, provide a body (e.g. a ReadableStream or an SSE-shaped Response).
- Check whether the server should actually have events; an empty/compressed response may indicate a server bug.
- Use a runtime/polyfill that supports response.body as a readable stream.
Example fix
// before
mockFetch = async () => new Response(null, { status: 200 });
// after
mockFetch = async () => new Response("data: {\"type\":\"run_started\"}\n\n", { status: 200, headers: { "content-type": "text/event-stream" } }); Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch(url); if (!res.body || typeof res.body.getReader !== "function") throw new Error("Response has no readable stream body"); Type guard
const hasReadableBody = (res) => !!res.body && typeof res.body.getReader === "function";
Try / catch
try { for await (const ev of client.fleetEvents(runId)) handle(ev); } catch (e) { if (e instanceof RuntimeApiError && e.message.includes("readable body")) { console.error("Check fetch mock/server response for a missing body stream"); } else throw e; } Prevention
- When mocking fetch for tests, always return a Response with a body
- Avoid polyfills that drop response.body
- Confirm the events endpoint returns text/event-stream
When it happens
Trigger: A GET to the events endpoint returns a response whose body is null or non-readable — typical with mock/stub HTTP clients or unusual proxies — while the request expected a streaming response.
Common situations: Testing with a fetch mock that returns { ok: true } without a body; an intermediary returning an empty 204-style response; a fetch polyfill that doesn't expose body streams.
Related errors
- ${compactRuntimeError(response.status, body)}
- SSE stream request failed: HTTP
- Stream read error
- Chat Completions stream closed before [DONE] or…
- Codewhale responses request should succeed
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/0c677e62c5b5f96c.
Report an issue: GitHub.
Appendix: source
Thrown at npm/runtime-sdk/index.js:117
options.path ?? `/v1/fleet/runs/${segment(runId)}/events`,
options,
);
const response = await this.#rawRequest(path, {
method: "GET",
capability: "fleet_event_stream",
accept: "text/event-stream",
});
const contentType = response.headers.get("content-type") ?? "";
if (contentType.includes("application/json")) {
const payload = await response.json();
const events = Array.isArray(payload) ? payload : (payload.events ?? []);
for (const event of events) {
yield event;
}
return;
}
if (!response.body) {
throw new RuntimeApiError("Runtime API event response did not include a readable body", {
method: "GET",
path,
});
}
for await (const event of parseEventStream(response.body)) {
yield event;
}
}
/** Read the existing durable thread journal. This never starts a turn. */
async *threadEvents(threadId, options = {}) {
const query = new URLSearchParams();
for (const [key, value] of [["since_seq", options.sinceSeq], ["replay_limit", options.replayLimit]]) {
if (value === undefined) continue;
if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${key} must be a nonnegative safe integer`);
query.set(key, String(value));
}
if (options.includeProgress !== undefined && typeof options.includeProgress !== "boolean")View on GitHub (pinned to 433685b202)