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
- Retry the whole request — a fresh create_session + events cycle fixes expired-session and transient 5xx cases
- Check the status code: 401/403 → refresh the Trae credential; 404 → session expired, recreate session; 429 → back off
- Remove/bypass proxies that strip or buffer streaming response bodies (res.body empty)
- Reduce delay between session creation and the events call so the session cannot expire
- 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
- Open the events stream immediately after session creation to avoid expiry
- Avoid proxies that strip or buffer streaming bodies
- Refresh credentials on 401/403 before retrying
- Treat timeouts and 5xx as retryable with a fresh session
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
- [${res.status}] ${text}
- Trae create_session: ${JSON.stringify(json)}
- Kiro tool input changed fragment type
- Kiro tool_use stop reason did not include a complete tool ca
- MiMo bootstrap failed: ${response.status}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/3ddc95a3897f5a42.
Report an issue: GitHub.