odysseus-dev/odysseus · warning · Error
HTTP ${r.status}
Error message
HTTP ${r.status} What it means
Thrown inside the MCP OAuth polling loop when GET /api/mcp/servers responds non-OK. The listed message 'HTTP ${r.status}' is the raw template literal — at runtime it interpolates the real status (e.g. 'HTTP 401'). The loop polls every 2s up to 90 tries; the throw is caught locally, counted in fails, and the loop continues (exits after ~12 consecutive failures via a fails check).
Source
Thrown at static/js/settings.js:4986
} finally {
_setBtnLoading(pasteGo, false, 'Submit');
}
});
}
// Drives the OAuth flow: waits for the auth_url (discovery+DCR may lag),
// opens it once, then resolves on connected/error.
async function _handleMcpAuth(id, initialAuthUrl, tries = 90) {
let opened = false;
const openAuth = (u) => { if (!opened && u) { opened = true; window.open(u, '_blank', 'noopener'); _showMcpPasteback(id); } };
openAuth(initialAuthUrl);
const msg = el('uf-mcp-msg');
let fails = 0;
for (let i = 0; i < tries; i++) {
await new Promise(res => setTimeout(res, 2000));
try {
const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' });
if (!r.ok) throw new Error('HTTP ' + r.status);
const list = await r.json();
fails = 0;
const s = Array.isArray(list) ? list.find(x => x.id === id) : null;
if (!s) continue;
if (s.auth_url) openAuth(s.auth_url);
if (s.status === 'connected') {
if (msg) msg.textContent = `Connected (${s.tool_count || 0} tools)`;
await renderList(); return;
}
if (s.status === 'error') {
if (msg) msg.textContent = `Failed: ${s.error || 'unknown'}`; return;
}
} catch (e) {
// Tolerate a single blip, but surface persistent failures instead of
// silently polling until timeout.
if (++fails >= 5 && msg) msg.textContent = `Status check failing (${e.message || 'network error'}) — still retrying…`;
}
}View on GitHub (pinned to f9235ebbf1)
Solutions
- Check whether the user completed the OAuth consent and the popup wasn't blocked
- Verify auth is still valid (re-login) if the network tab shows 401s
- Confirm the backend stayed up through the flow; retry the connect after it stabilizes
- Distinguish terminal statuses (401/403 should abort with a re-login prompt) from transient 5xx (keep polling)
Example fix
// before
const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' });
if (!r.ok) throw new Error('HTTP ' + r.status);
// after
const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' });
if (r.status === 401 || r.status === 403) { if (msg) msg.textContent = 'Signed out — please log in again'; return; }
if (!r.ok) throw new Error('HTTP ' + r.status); Defensive patterns
Strategy: retry
Validate before calling
// pre-flight before opening the OAuth popup
try {
const pre = await fetch('/api/mcp/servers', { credentials: 'same-origin' });
if (!pre.ok) { if (msg) msg.textContent = `Not ready (HTTP ${pre.status})`; return; }
} catch { if (msg) msg.textContent = 'Server unreachable'; return; } Try / catch
try { const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' }); if (r.status === 401 || r.status === 403) { if (msg) msg.textContent = 'Signed out — log in again'; return; } if (!r.ok) throw new Error('HTTP ' + r.status); ... } catch (e) { fails++; if (fails > 12) { if (msg) msg.textContent = `Giving up: ${e.message}`; return; } } Prevention
- Treat 401/403 as terminal — abort with a re-login prompt instead of polling
- Keep the bounded failure counter (~12) so transient 5xx retries don't spin forever
- Warn when window.open is blocked — the flow can never complete without the popup
- Poll with backoff instead of a fixed 2s cadence to be kinder to a restarting backend
When it happens
Trigger: GET /api/mcp/servers returning 401 (auth cookie expired mid-flow), 502/503 while the backend restarts, or the fetch rejecting — repeated during the polling window after the OAuth popup opened.
Common situations: Backend redeploy during OAuth enrollment; auth session expiring while the user sits on the IdP consent screen; reverse proxy briefly 502ing; the loop's own 2s cadence racing a slow DCR handshake.
Related errors
- Failed to save
- No email account is configured for the authenticated owner
- GitHub device-code request failed: {e}
- Unknown or expired login session
- Invalid OAuth {field_name}: path must stay under {base}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/bb1c4e71b3c56ac2.
Report an issue: GitHub.