QuantumNous/new-api · warning · Error

Failed to sign out session

Error message

Failed to sign out session

What it means

Thrown by the revoke-session mutation in the profile Login Sessions card when the backend API call revokeLoginSession(sid) either rejects (network/HTTP failure) or resolves with success=false. The error text is only the fallback; when the server supplies response.message that message is used instead. It surfaces to the user via onError -> toast.error.

Source

Thrown at web/src/features/profile/components/login-sessions-card.tsx:80

  const [revokeTarget, setRevokeTarget] = useState<LoginSession | null>(null)
  const [confirmOthers, setConfirmOthers] = useState(false)

  const sessionsQuery = useQuery({
    queryKey: sessionQueryKey,
    queryFn: async () => {
      const response = await getLoginSessions()
      if (!response.success) {
        throw new Error(response.message || t('Failed to load login sessions'))
      }
      return response.data ?? []
    },
  })

  const revokeMutation = useMutation({
    mutationFn: async (sid: string) => {
      const response = await revokeLoginSession(sid)
      if (!response.success) {
        throw new Error(response.message || t('Failed to sign out session'))
      }
      return sid
    },
    onSuccess: async (sid) => {
      const revokedCurrent = sessionsQuery.data?.some(
        (session) => session.sid === sid && session.current
      )
      setRevokeTarget(null)
      if (revokedCurrent) {
        clearAuthenticatedClientState(queryClient)
        void navigate({ to: '/sign-in', replace: true })
        return
      }
      toast.success(t('Session signed out'))
      await queryClient.invalidateQueries({ queryKey: sessionQueryKey })
    },
    onError: (error: Error) => toast.error(error.message),
  })

View on GitHub (pinned to e2c7aa7b10)

Solutions

  1. Open the browser network tab and inspect the revoke request's status code and body; the real cause is in response.message, not the fallback text.
  2. If the session list is stale, refresh it (the panel already invalidates sessionQueryKey) and retry revoke against a sid that still exists.
  3. If the auth token expired, sign in again; the API client should redirect on 401.
  4. Verify the backend route for session revocation is enabled and the session store (e.g. Redis for multi-node) is reachable, since session state must be shared across instances.

Example fix

// before
const response = await revokeLoginSession(sid)
if (!response.success) {
  throw new Error(response.message || t('Failed to sign out session'))
}
// after: treat 'already revoked' as success so stale lists don't error
const response = await revokeLoginSession(sid)
if (!response.success && !/not found|already/i.test(response.message ?? '')) {
  throw new Error(response.message || t('Failed to sign out session'))
}
Defensive patterns

Strategy: try-catch

Try / catch

onError: (error: Error) => {
  const msg = error.message || t('Failed to sign out session')
  if (/not found|already (revoked|signed out)/i.test(msg)) {
    void queryClient.invalidateQueries({ queryKey: sessionQueryKey })
    return
  }
  toast.error(msg)
}

Prevention

When it happens

Trigger: POST to the session-revoke endpoint for a specific sid while the session was already revoked/expired server-side, the auth token is invalid or expired (401), the sid does not belong to the current user, or the API is unreachable (network error / proxy down).

Common situations: Session list is stale (session already revoked in another tab or by the server's session TTL), the user's token was refreshed mid-flight, logging out from a device whose session row was already purged, or a dev environment where the Go backend is not running.

Related errors


AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15). Data as JSON: /api/errors/fd6a9ce5ac8924af. Report an issue: GitHub.