{"record":{"id":"986de777e537449c","repo":"odysseus-dev/odysseus","slug":"login-failed","errorCode":null,"errorMessage":"Login failed","messagePattern":"Login failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/login.html","lineNumber":499,"sourceCode":"        fetch('/api/auth/features', { credentials: 'same-origin' }).then(r => r.json()),\n        fetch('/api/auth/settings', { credentials: 'same-origin' }).then(r => r.json()),\n      ]).then(([sess, feat, sett]) => {\n        sessionStorage.setItem('ody-prefetch-sessions', JSON.stringify(sess));\n        sessionStorage.setItem('ody-prefetch-features', JSON.stringify(feat));\n        sessionStorage.setItem('ody-prefetch-settings', JSON.stringify(sett));\n      }).catch(() => {}).finally(() => { window.location.replace('/'); });\n    }\n    async function doLogin(totpCode) {\n      const loginBody = { username, password, remember };\n      if (totpCode) loginBody.totp_code = totpCode;\n      const res = await fetch('/api/auth/login', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        credentials: 'same-origin',\n        body: JSON.stringify(loginBody)\n      });\n      const data = await res.json();\n      if (!res.ok) throw new Error(data.detail || 'Login failed');\n      return data;\n    }\n    try {\n      let data = await doLogin();\n      // 2FA required — show TOTP input\n      if (data.requires_totp) {\n        submitBtn.disabled = false;\n        // Only add TOTP input once\n        if (document.getElementById('totp-input')) return;\n        form._totpMode = true;\n        const totpWrap = document.createElement('div');\n        totpWrap.style.cssText = 'margin-top:12px;';\n        totpWrap.innerHTML = '<label for=\"totp-input\" style=\"font-size:0.85em;opacity:0.7;display:block;margin-bottom:4px;\">2FA Code</label><input type=\"text\" id=\"totp-input\" placeholder=\"Enter 6-digit code\" aria-label=\"Two-factor authentication code\" autocomplete=\"one-time-code\" inputmode=\"numeric\" maxlength=\"8\" style=\"width:100%;padding:10px 12px;background:var(--bg);color:var(--fg);border:1px solid var(--border);border-radius:8px;font-size:14px;box-sizing:border-box;text-align:center;letter-spacing:4px;\">';\n        const formEl = submitBtn.parentElement;\n        formEl.insertBefore(totpWrap, submitBtn);\n        const totpInput = document.getElementById('totp-input');\n        totpInput.focus();\n        submitBtn.textContent = 'Verify';","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/login.html#L481-L517","documentation":"Fallback error from doLogin() when POST /api/auth/login returns non-2xx. The server's data.detail is preferred (e.g. 'invalid credentials'); this literal appears only when the body lacks detail. The thrown error is caught by the outer try and rendered in the login form.","triggerScenarios":"Wrong username/password (backend returns detail like 'Invalid credentials'), account locked, or a non-JSON response (gateway error) leaving data.detail undefined.","commonSituations":"Typo in credentials; password changed elsewhere; expired/locked account; API fronted by a proxy that returns HTML errors.","solutions":["Verify username and password; the specific server reason usually replaces this message","If the generic message persists, inspect the raw response body/status in devtools (proxy or 5xx issue)","Confirm the account exists and is not locked; reset password if needed","Ensure cookies/JS are enabled so the login POST and session work"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const data = await doLogin(); if (data.requires_totp) showTotpStep(); } catch (err) { errEl.textContent = err.message || 'Login failed'; submitBtn.disabled = false; }","preventionTips":["Read the server-provided detail text for the true failure cause","Inspect the raw response when only the generic message shows (likely proxy/5xx)","Handle requires_totp before treating the call as failed"],"tags":["authentication","login","frontend"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}