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

  1. Check whether the user completed the OAuth consent and the popup wasn't blocked
  2. Verify auth is still valid (re-login) if the network tab shows 401s
  3. Confirm the backend stayed up through the flow; retry the connect after it stabilizes
  4. 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

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


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