Hmbown/CodeWhale · error · RuntimeApiError

Runtime thread response is not an event stream

Error message

Runtime thread response is not an event stream

What it means

After issuing the GET events request, threadEvents() verifies the response is actually a text/event-stream with a readable body. If the content-type is not SSE (or the body is missing), it cancels the body and throws RuntimeApiError. This typically means an intermediary or the server responded with a regular HTTP response (HTML error page, JSON error) instead of the event stream the SDK requires.

Solutions

  1. Verify the base URL points at the Codewhale runtime API and the runtime supports thread_event_stream.
  2. Check the runtime version; upgrade to one that implements the thread event SSE endpoint.
  3. Inspect response handling in your proxy/auth layer so SSE responses pass through unmodified.
  4. Catch RuntimeApiError and log body/status from the error options to see what was actually returned.

Example fix

// before
const events = client.threadEvents(id);
// after
try {
  for await (const ev of client.threadEvents(id)) handle(ev);
} catch (err) {
  if (err instanceof RuntimeApiError) console.error(err.message, err.status, err.body);
  throw err;
}
Defensive patterns

Strategy: try-catch

Try / catch

try { for await (const ev of client.threadEvents(id)) handle(ev); } catch (err) { if (err instanceof RuntimeApiError && err.message.includes("not an event stream")) { log.error("non-SSE response from runtime", err.status, err.body); } throw err; }

Prevention

When it happens

Trigger: Calling threadEvents() against a runtime that does not implement the thread_event_stream capability and returns a 404/JSON body; a proxy or auth gateway intercepting the request and returning an HTML error page; misconfigured base URL pointing at a non-runtime endpoint.

Common situations: Reverse proxy returning an error/login page; older runtime version without SSE support on /v1/threads/:id/events; wrong port or path prefix in baseUrl.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/101c1844cf2c6f6e. Report an issue: GitHub.

Appendix: source

Thrown at npm/runtime-sdk/index.js:145

  /** 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")
      throw new TypeError("includeProgress must be a boolean");
    if (options.includeProgress) query.set("progress", "true");
    const path = `/v1/threads/${segment(threadId)}/events?${query}`;
    const response = await this.#rawRequest(path, {
      method: "GET", capability: "thread_event_stream", accept: "text/event-stream",
      signal: options.signal, redirect: "error",
    });
    if (!response.body || !/^text\/event-stream(?:;|$)/i.test(response.headers.get("content-type") ?? "")) {
      await response.body?.cancel();
      throw new RuntimeApiError("Runtime thread response is not an event stream", { method: "GET", path });
    }
    if (options.includeProgress && response.headers.get("x-codewhale-event-progress") !== "1") {
      await response.body.cancel();
      throw new RuntimeCapabilityError("thread_event_progress", "Runtime does not support thread replay progress", { method: "GET", path, status: 501 });
    }
    yield* parseEventStream(response.body, { maxFrameChars: 2 * 1024 * 1024, requireBoundary: true });
  }

  async #jsonRequest(path, options = {}) {
    const response = await this.#rawRequest(path, options);
    if (response.status === 204) {
      return null;
    }
    return response.json();
  }

  async #rawRequest(path, options = {}) {
    const method = options.method ?? "GET";

View on GitHub (pinned to 433685b202)