odysseus-dev/odysseus · error · Error

HTTP ${response.status}

Error message

HTTP ${response.status}

What it means

Error thrown when the compare-mode streaming request POST /api/chat_stream returns a non-OK status before any SSE/stream data is read. The message is only 'HTTP <status>' — no response body is inspected, so the cause is opaque from the UI.

Source

Thrown at static/js/compare/stream.js:261

    }
    const incognitoChk = document.getElementById('incognito-toggle');
    if (incognitoChk && incognitoChk.checked) {
      fd.append('incognito', 'true');
    }
    // Disable document tool and memory injection in compare mode
    fd.append('no_documents', 'true');
    fd.append('no_memory', 'true');
    // Tell backend this is compare mode — strip all non-toggled tools
    fd.append('compare_mode', 'true');
    // Forward preset if selected
    if (presetsModule && presetsModule.getSelectedPreset()) {
      fd.append('preset_id', presetsModule.getSelectedPreset());
    }

    const response = await fetch(`${state.API_BASE}/api/chat_stream`, {
      method: 'POST', body: fd, signal: ac.signal
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      _resetIdleTimeout();  // any chunk = stream is alive

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split('\n');
      buffer = lines.pop() || '';

      for (const line of lines) {
        if (!line.startsWith('data: ')) continue;
        const data = line.slice(6);
        if (data === '[DONE]') break;

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Reproduce with curl -i -X POST the same multipart body to read the status and error body the code discards
  2. Verify the session id sent in the form actually exists (it should be the one returned by POST /api/session)
  3. If a preset_id is appended, confirm that preset still exists server-side or drop it
  4. Improve the error to include the body: read response.text() before throwing

Example fix

// before
if (!response.ok) throw new Error(`HTTP ${response.status}`);

// after
if (!response.ok) {
  const body = await response.text().catch(() => '');
  let detail = body;
  try { detail = JSON.parse(body).detail || body; } catch {}
  throw new Error(`HTTP ${response.status}: ${String(detail).slice(0, 200)}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!fd.get('session')) { throw new Error('No session id to stream against'); }
if (presetsModule?.getSelectedPreset && presetsModule.getSelectedPreset() == null) fd.delete('preset_id');

Try / catch

try {
  const response = await fetch(`${state.API_BASE}/api/chat_stream`, { method: 'POST', body: fd, signal: ac.signal });
  if (!response.ok) {
    const body = await response.text().catch(() => '');
    let detail = body;
    try { detail = JSON.parse(body).detail || body; } catch {}
    throw new Error(`HTTP ${response.status}: ${String(detail).slice(0, 200)}`);
  }
  // ... read stream
} catch (e) {
  if (e.name === 'AbortError') return; // user cancelled — not an error
  showCompareError(e.message);
}

Prevention

When it happens

Trigger: POST /api/chat_stream (multipart with no_documents, no_memory, compare_mode, optional preset_id) returns 404 (unknown session), 400 (invalid preset_id), 422 (missing fields), or 500 (backend exception before the stream starts).

Common situations: Session id from the create call is stale or invalid; preset selected in the UI was deleted server-side; backend restarted mid-compare; reverse proxy (nginx) returns 502/504 before the stream begins; compare_mode flag unsupported on older backend.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/d9340d76548e2aa7. Report an issue: GitHub.