makeplane/plane · critical · AppError
Authentication unsuccessful
Error message
Authentication unsuccessful
What it means
The catch-all re-throw at the bottom of handleAuthentication. Any error inside the auth flow that is not the explicit user-mismatch (including a thrown AUTH_USER_MISMATCH that gets re-wrapped here) is normalized to the generic message 'Authentication unsuccessful' with the original code preserved via `appError.code`. This is the error a live-connection client sees whenever authentication fails for any reason other than missing credentials.
Source
Thrown at apps/live/src/lib/auth.ts:95
try {
const userService = new UserService();
const user = await userService.currentUser(cookie);
if (user.id !== userId) {
throw new AppError("Authentication unsuccessful: User ID mismatch", { code: "AUTH_USER_MISMATCH" });
}
return {
user: {
id: user.id,
name: user.display_name,
},
};
} catch (error) {
const appError = new AppError(error, {
context: { operation: "handleAuthentication" },
});
logger.error("Authentication failed", appError);
throw new AppError("Authentication unsuccessful", { code: appError.code });
}
};
View on GitHub (pinned to 1c8a60f858)
Solutions
- Read the server logs: `logger.error("Authentication failed", appError)` on line 94 logs the underlying error with its context — the generic client message hides it, the log does not.
- If the code is AUTH_USER_MISMATCH, follow error [0]; if it is undefined/empty, the underlying error was a non-AppError (likely a network or HTTP error from currentUser).
- Confirm the cookie is still valid by making a REST `/users/me/` call from the same browser; if that 401s, the session is dead — re-authenticate.
- Verify connectivity between the live service and the API (API_BASE_URL, internal service DNS, proxy headers forwarded).
Example fix
// before: the catch loses the real cause for the client
throw new AppError("Authentication unsuccessful", { code: appError.code });
// after: preserve the cause so callers/UI can branch on it
throw new AppError("Authentication unsuccessful", { code: appError.code, cause: appError }); Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the cookie still resolves to a user before opening the live socket
const ok = await fetch('/api/users/me/', { credentials: 'include' });
if (!ok.ok) { await reauthenticate(); } Try / catch
try { await connectLiveDocument(); }
catch (e) {
// The generic message hides the cause; check server logs for the real error.
if (e instanceof AppError && (e.code === 'AUTH_USER_MISMATCH' || !e.code)) {
await reauthenticate();
} else { notifyTransientError(e); }
} Prevention
- Treat any live auth failure as 'session is dead' and force re-authentication.
- Server-side: preserve the cause in AppError so clients can branch on it.
- Log the underlying error with request id for correlation with the generic client message.
When it happens
Trigger: userService.currentUser(cookie) rejects (network error, 401/403 from the API, expired cookie, backend 5xx); or the explicit AUTH_USER_MISMATCH thrown at line 81 is caught here and re-thrown as the generic message. Also fires if AppError construction itself receives a non-Error / undefined and `appError.code` resolves to undefined.
Common situations: Cookie expired or was cleared server-side (session revoked) while the websocket tab stayed open; the API host is unreachable from the live service (network/DNS/CORS between live pod and api pod); the user's account was deactivated mid-session; mis-configuration of the live service's API base URL so currentUser() 404s.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- AUTH_USER_MISMATCH
- AUTH_MISSING_CREDENTIALS
- Given API token is not valid
- Given API token is not valid
- 5005
AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12).
Data as JSON: /api/errors/86d32f67d3b7bec2.
Report an issue: GitHub.