{"record":{"id":"0c677e62c5b5f96c","repo":"Hmbown/CodeWhale","slug":"runtime-api-event-response-did-not-include-a-readable-body","errorCode":null,"errorMessage":"Runtime API event response did not include a readable body","messagePattern":"Runtime API event response did not include a readable body","errorType":"http","errorClass":"RuntimeApiError","httpStatus":null,"severity":"error","filePath":"npm/runtime-sdk/index.js","lineNumber":117,"sourceCode":"      options.path ?? `/v1/fleet/runs/${segment(runId)}/events`,\n      options,\n    );\n    const response = await this.#rawRequest(path, {\n      method: \"GET\",\n      capability: \"fleet_event_stream\",\n      accept: \"text/event-stream\",\n    });\n    const contentType = response.headers.get(\"content-type\") ?? \"\";\n    if (contentType.includes(\"application/json\")) {\n      const payload = await response.json();\n      const events = Array.isArray(payload) ? payload : (payload.events ?? []);\n      for (const event of events) {\n        yield event;\n      }\n      return;\n    }\n    if (!response.body) {\n      throw new RuntimeApiError(\"Runtime API event response did not include a readable body\", {\n        method: \"GET\",\n        path,\n      });\n    }\n    for await (const event of parseEventStream(response.body)) {\n      yield event;\n    }\n  }\n\n  /** Read the existing durable thread journal. This never starts a turn. */\n  async *threadEvents(threadId, options = {}) {\n    const query = new URLSearchParams();\n    for (const [key, value] of [[\"since_seq\", options.sinceSeq], [\"replay_limit\", options.replayLimit]]) {\n      if (value === undefined) continue;\n      if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${key} must be a nonnegative safe integer`);\n      query.set(key, String(value));\n    }\n    if (options.includeProgress !== undefined && typeof options.includeProgress !== \"boolean\")","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/npm/runtime-sdk/index.js#L99-L135","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmockFetch = async () => new Response(null, { status: 200 });\n// after\nmockFetch = async () => new Response(\"data: {\\\"type\\\":\\\"run_started\\\"}\\n\\n\", { status: 200, headers: { \"content-type\": \"text/event-stream\" } });","handlingStrategy":"try-catch","validationCode":"const res = await fetch(url); if (!res.body || typeof res.body.getReader !== \"function\") throw new Error(\"Response has no readable stream body\");","typeGuard":"const hasReadableBody = (res) => !!res.body && typeof res.body.getReader === \"function\";","tryCatchPattern":"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; }","preventionTips":["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"],"tags":["streaming","sse","http","runtime-api"],"backgroundTag":"empty-response-body","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}