{"record":{"id":"bb1c4e71b3c56ac2","repo":"odysseus-dev/odysseus","slug":"http-r-status-bb1c4e","errorCode":null,"errorMessage":"HTTP ${r.status}","messagePattern":"HTTP \\$\\{r\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"static/js/settings.js","lineNumber":4986,"sourceCode":"        } finally {\n          _setBtnLoading(pasteGo, false, 'Submit');\n        }\n      });\n    }\n\n    // Drives the OAuth flow: waits for the auth_url (discovery+DCR may lag),\n    // opens it once, then resolves on connected/error.\n    async function _handleMcpAuth(id, initialAuthUrl, tries = 90) {\n      let opened = false;\n      const openAuth = (u) => { if (!opened && u) { opened = true; window.open(u, '_blank', 'noopener'); _showMcpPasteback(id); } };\n      openAuth(initialAuthUrl);\n      const msg = el('uf-mcp-msg');\n      let fails = 0;\n      for (let i = 0; i < tries; i++) {\n        await new Promise(res => setTimeout(res, 2000));\n        try {\n          const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' });\n          if (!r.ok) throw new Error('HTTP ' + r.status);\n          const list = await r.json();\n          fails = 0;\n          const s = Array.isArray(list) ? list.find(x => x.id === id) : null;\n          if (!s) continue;\n          if (s.auth_url) openAuth(s.auth_url);\n          if (s.status === 'connected') {\n            if (msg) msg.textContent = `Connected (${s.tool_count || 0} tools)`;\n            await renderList(); return;\n          }\n          if (s.status === 'error') {\n            if (msg) msg.textContent = `Failed: ${s.error || 'unknown'}`; return;\n          }\n        } catch (e) {\n          // Tolerate a single blip, but surface persistent failures instead of\n          // silently polling until timeout.\n          if (++fails >= 5 && msg) msg.textContent = `Status check failing (${e.message || 'network error'}) — still retrying…`;\n        }\n      }","sourceCodeStart":4968,"sourceCodeEnd":5004,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/settings.js#L4968-L5004","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\n          const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' });\n          if (!r.ok) throw new Error('HTTP ' + r.status);\n\n// after\n          const r = await fetch('/api/mcp/servers', { credentials: 'same-origin' });\n          if (r.status === 401 || r.status === 403) { if (msg) msg.textContent = 'Signed out — please log in again'; return; }\n          if (!r.ok) throw new Error('HTTP ' + r.status);","handlingStrategy":"retry","validationCode":"// pre-flight before opening the OAuth popup\ntry {\n  const pre = await fetch('/api/mcp/servers', { credentials: 'same-origin' });\n  if (!pre.ok) { if (msg) msg.textContent = `Not ready (HTTP ${pre.status})`; return; }\n} catch { if (msg) msg.textContent = 'Server unreachable'; return; }","typeGuard":null,"tryCatchPattern":"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; } }","preventionTips":["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"],"tags":["mcp","oauth","polling","network","auth"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}