decolua/9router · error

[${res.status}] events stream failed

Error message

[${res.status}] events stream failed

What it means

After creating a session, Trae streams results over a GET /events SSE connection. streamEvents() aborts on timeout/signal and throws this error when the events response is not OK or has no body, embedding only the status code — without a readable event stream the executor cannot deliver any response.

Source

Thrown at open-sse/executors/trae.js:145

    const text = await res.text();
    if (!res.ok) throw new Error(`[${res.status}] ${text}`);
    const json = JSON.parse(text);
    if (json?.code !== 0) throw new Error(`Trae create_session: ${JSON.stringify(json)}`);
    return { sessionId: json.data.chat_session_id, messageId: json.data.message_id };
  }

  // GET /events SSE → invoke onEvent(eventType, dataObj) per frame.
  // Resolves when `done`/`error` arrives, the stream ends, or timeout fires.
  async streamEvents(headers, sessionId, replyTo, onEvent, signal) {
    const url = `${this.base()}/chat_sessions/${sessionId}/events?reply_to_message_id=${encodeURIComponent(replyTo)}`;
    const ctrl = new AbortController();
    if (signal?.aborted) ctrl.abort();
    const timer = setTimeout(() => ctrl.abort(new Error("trae stream timeout")), STREAM_TIMEOUT_MS);
    const onAbort = () => ctrl.abort();
    if (signal) signal.addEventListener("abort", onAbort, { once: true });
    try {
      const res = await proxyAwareFetch(url, { method: "GET", headers, signal: ctrl.signal }, null);
      if (!res.ok || !res.body) throw new Error(`[${res.status}] events stream failed`);
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buf = "";
      let ev = null;
      for (;;) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += decoder.decode(value, { stream: true });
        let nl;
        while ((nl = buf.indexOf("\n")) >= 0) {
          const line = buf.slice(0, nl).replace(/\r$/, "");
          buf = buf.slice(nl + 1);
          if (line.startsWith("event:")) ev = line.slice(6).trim();
          else if (line.startsWith("data:")) {
            const payload = line.slice(5).trim();
            let data;
            try { data = JSON.parse(payload); } catch { data = { _raw: payload }; }
            if (onEvent(ev, data)) {

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Retry the whole request — a fresh create_session + events cycle fixes expired-session and transient 5xx cases
  2. Check the status code: 401/403 → refresh the Trae credential; 404 → session expired, recreate session; 429 → back off
  3. Remove/bypass proxies that strip or buffer streaming response bodies (res.body empty)
  4. Reduce delay between session creation and the events call so the session cannot expire
  5. Update 9router in case Trae changed the events endpoint or auth requirements

Example fix

// before
const events = await executor.sse(...); // throws raw
// after
let events;
try { events = await executor.sse(...); }
catch (e) {
  if (/\[404\] events stream failed/.test(e.message)) {
    events = await retryWithNewSession(...); // full retry recreates the session
  } else throw e;
}
Defensive patterns

Strategy: retry

Try / catch

try {
  await executor.sse(...);
} catch (e) {
  const status = +String(e.message).match(/^\[(\d+)\]/)?.[1] ?? 0;
  if (status === 404 || status >= 500) {
    // session likely expired or transient — full retry recreates the session
    return retryWithNewSession(req);
  }
  if (status === 401 || status === 403) { await refreshTraeCredential(); return retry(); }
  throw e;
}

Prevention

When it happens

Trigger: The GET /events request returns a non-2xx status (session already expired, auth rejected, 429, 5xx) or succeeds without a body (res.body null, e.g. some proxies stripping streaming responses). Raised from streamEvents, called by sse/execute.

Common situations: Session expired between create_session and the events call (slow client or long queue); Trae rejecting the events request due to auth/region; a proxy or CDN buffering/stripping streaming bodies; transient 5xx right after session creation.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/3ddc95a3897f5a42. Report an issue: GitHub.