{"record":{"id":"01666667896013d7","repo":"QuantumNous/new-api","slug":"login-failed-016666","errorCode":null,"errorMessage":"Login failed","messagePattern":"Login failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/features/auth/sign-in/components/user-auth-form.tsx","lineNumber":187,"sourceCode":"    try {\n      const res = await login({\n        username: data.username,\n        password: data.password,\n        turnstile: submittedTurnstileToken,\n      })\n\n      if (res.success) {\n        if (res.data && 'require_2fa' in res.data && res.data.require_2fa) {\n          if (!res.data.flow_token) {\n            throw new Error(t('Login flow expired. Please sign in again.'))\n          }\n          setPending2FAFlowToken(res.data.flow_token)\n          redirectTo2FA()\n          return\n        }\n\n        if (!isAuthBundle(res.data)) {\n          throw new Error(t('Login failed'))\n        }\n        await handleLoginSuccess(res.data, redirectTo)\n        toast.success(t('Welcome back!'))\n      }\n    } catch (error: unknown) {\n      if (axios.isAxiosError(error)) return\n      toast.error(error instanceof Error ? error.message : loginFailedMessage)\n    } finally {\n      setIsLoading(false)\n    }\n  }\n\n  const handleOpenWeChatDialog = () => {\n    if (requiresLegalConsent && !agreedToLegal) {\n      toast.error(legalConsentErrorMessage)\n      return\n    }\n","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/auth/sign-in/components/user-auth-form.tsx#L169-L205","documentation":"Thrown in the sign-in form when a successful, non-2FA login response fails the isAuthBundle structural check — res.data lacks the required auth bundle fields (access token, session, user). It protects handleLoginSuccess from persisting a malformed bundle that would immediately break authenticated requests.","triggerScenarios":"POST login returns success:true but data is null, partial (missing session/user/token), or an unexpected shape (e.g. only {require_2fa:false}).","commonSituations":"Backend contract drift after an upgrade (renamed or restructured login payload); response transformed by an interceptor; backend returning success:true on a degraded path without a full bundle.","solutions":["Log res.data shape and compare with isAuthBundle's required fields; the check is the source of truth for the expected contract.","Align backend login response with the AuthBundle shape (access_token, token_type, access_expires_at, session, user) or update isAuthBundle after a deliberate contract change.","Check axios response interceptors are not unwrapping/replacing data.","Ensure frontend/backend deploy together."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// isAuthBundle is the guard; keep it authoritative for the login contract\nif (res.success && !isAuthBundle(res.data)) { /* contract violation path */ }","tryCatchPattern":"try {\n  if (res.success && !isAuthBundle(res.data)) throw new Error(t('Login failed'))\n} catch (error) {\n  if (axios.isAxiosError(error)) return // transport errors handled elsewhere\n  toast.error(error instanceof Error ? error.message : loginFailedMessage)\n}","preventionTips":["Share one AuthBundle schema between password, 2FA, and passkey login paths","Pin the backend login response with a consumer-driven contract test"],"tags":["login","auth-bundle","type-guard","api-contract"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}