{"record":{"id":"0cc33e6f2532d384","repo":"QuantumNous/new-api","slug":"failed-to-sign-out-session","errorCode":null,"errorMessage":"Failed to sign out session","messagePattern":"Failed to sign out session","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"web/src/features/auth/hooks/use-oauth-login.ts","lineNumber":65,"sourceCode":"  const [isTelegramPending, setIsTelegramPending] = useState(false)\n  const [githubButtonText, setGithubButtonText] = useState('')\n  const [githubButtonDisabled, setGithubButtonDisabled] = useState(false)\n  const githubTimeoutRef = useRef<NodeJS.Timeout | null>(null)\n\n  useEffect(() => {\n    setGithubButtonText(t('Continue with GitHub'))\n\n    return () => {\n      if (githubTimeoutRef.current) {\n        clearTimeout(githubTimeoutRef.current)\n      }\n    }\n  }, [t])\n\n  const resetSession = async () => {\n    const response = await logout()\n    if (!response.success) {\n      throw new Error(response.message || t('Failed to sign out session'))\n    }\n    clearAuthentication()\n  }\n\n  const handleGitHubLogin = async () => {\n    if (!status?.github_client_id) return\n    if (githubButtonDisabled) return\n\n    setIsLoading(true)\n    setGithubButtonDisabled(true)\n    setGithubButtonText(t('Redirecting to GitHub...'))\n\n    if (githubTimeoutRef.current) {\n      clearTimeout(githubTimeoutRef.current)\n    }\n\n    githubTimeoutRef.current = setTimeout(() => {\n      setIsLoading(false)","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/auth/hooks/use-oauth-login.ts#L47-L83","documentation":"Thrown by resetSession() in use-oauth-login.ts when the logout API responds with success falsy. The hook treats a failed logout as fatal before clearing local authentication state, so the user stays signed in locally even though they asked to sign out. The thrown message prefers the server's message and falls back to the translated generic string.","triggerScenarios":"Calling resetSession() while the session token is already expired or revoked (backend rejects POST /api/user/logout); backend temporarily unavailable behind the same origin; logout endpoint returning success:false for an invalid session.","commonSituations":"User clicks 'switch account' on the OAuth screen after their token expired in another tab; backend restarted losing in-memory session store; reverse proxy returning 502 bodies parsed as {success:false}.","solutions":["Check the logout request's response in DevTools — an expired/invalid session usually still succeeds; a 4xx/5xx indicates the real problem.","Decide whether local sign-out should proceed even when the server call fails: clearAuthentication() is currently skipped, keeping stale local state.","Retry the logout after re-authenticating or when the backend is reachable.","If the backend returns success:false for already-invalid sessions, treat that case as success client-side."],"exampleFix":"// before\nconst resetSession = async () => {\n  const response = await logout()\n  if (!response.success) {\n    throw new Error(response.message || t('Failed to sign out session'))\n  }\n  clearAuthentication()\n}\n\n// after — always clear local state; surface server failure as a toast, not a blocker\nconst resetSession = async () => {\n  try {\n    const response = await logout()\n    if (!response.success) {\n      toast.error(response.message || t('Failed to sign out session'))\n    }\n  } finally {\n    clearAuthentication()\n  }\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await resetSession()\n} catch {\n  // still clear local state so the user is effectively signed out client-side\n  clearAuthentication()\n}","preventionTips":["Always clearAuthentication() in a finally block so local sign-out never dead-ends","Treat 'session already invalid' logout failures as success","Show server logout failures as warnings, not blockers, during account-switch flows"],"tags":["authentication","logout","session","frontend"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}