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

  1. 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.
  2. 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).
  3. 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.
  4. 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

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

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/86d32f67d3b7bec2. Report an issue: GitHub.