BloopAI/vibe-kanban · error
Session expired. Please log in again.
Error message
Session expired. Please log in again.
What it means
makeAuthenticatedRequest in relayBackendApi.ts throws this when a relay/API call returns HTTP 401 and an attempted token refresh via authRuntime.triggerRefresh() fails to yield a new token. It signals the browser session (access + refresh tokens) is no longer usable and the user must re-authenticate interactively.
Source
Thrown at packages/web-core/src/shared/lib/relayBackendApi.ts:161
const response = await fetch(`${baseUrl}${path}`, {
...options,
headers,
credentials: 'include',
});
if (response.status === 401 && retryOn401) {
const newToken = await authRuntime.triggerRefresh();
if (newToken) {
headers.set('Authorization', `Bearer ${newToken}`);
return fetch(`${baseUrl}${path}`, {
...options,
headers,
credentials: 'include',
});
}
throw new Error('Session expired. Please log in again.');
}
return response;
}
async function parseErrorResponse(
response: Response,
fallbackMessage: string
): Promise<Error> {
try {
const body = await response.json();
const message = body.error || body.message || fallbackMessage;
return new Error(`${message} (${response.status} ${response.statusText})`);
} catch {
return new Error(
`${fallbackMessage} (${response.status} ${response.statusText})`
);
}View on GitHub (pinned to 4deb7eca8f)
Solutions
- Redirect the user to the login flow on catching this error (message instructs re-login).
- Inspect authRuntime.triggerRefresh() — ensure the refresh endpoint and refresh cookie are correctly configured and the server accepts it.
- Check that the relay/remote backend accepts the Authorization bearer token version (X-Client-Version mismatch can cause 401s).
- Clear stale auth state (tokens/cookies) before re-login to avoid refresh loops.
Example fix
// before
try {
await createRemoteSession(hostId);
} catch (e) { console.error(e); }
// after
try {
await createRemoteSession(hostId);
} catch (e) {
if (e instanceof Error && e.message.includes('Session expired')) {
authRuntime.logout();
window.location.assign('/login?reason=session-expired');
}
} Defensive patterns
Strategy: try-catch
Validate before calling
const authRuntime = getAuthRuntime(); const token = await authRuntime.getToken(); if (!token) redirectToLogin(); // no point calling if not even a token exists
Type guard
function isSessionExpiredError(e: unknown): e is Error {
return e instanceof Error && e.message.includes('Session expired');
} Try / catch
try {
await createRemoteSession(hostId);
} catch (e) {
if (isSessionExpiredError(e)) {
await authRuntime.logout();
window.location.assign('/login?reason=session-expired');
} else { throw e; }
} Prevention
- Gate API calls behind an auth-ready flag from the auth runtime
- Proactively refresh the token before expiry (scheduled refresh) instead of waiting for 401
- Ensure refresh cookies are sent (SameSite=None; Secure; credentials:'include')
- Redirect to login immediately on this error to avoid retry loops
When it happens
Trigger: Any authenticated relay call (e.g. createRemoteSession or makeAuthenticatedRelaySessionRequest for SPAKE2 enrollment/finish/signing refresh) receives a 401, and authRuntime.triggerRefresh() returns null/undefined — refresh token missing, expired, or rejected by the server.
Common situations: User leaves the app open past refresh-token lifetime; refresh cookie cleared or blocked (third-party cookie settings) so credentials:'include' sends nothing; server rotated/revoked the refresh token (logged in elsewhere); clock skew; relay backend restarted with in-memory sessions.
Related errors
- Session expired. Please log in again.
- Unauthorized
- Failed to accept invitation (${res.status})
- Failed to list organizations (${res.status})
- Failed to fetch identity (${res.status})
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/eae1016dc1a26b4e.
Report an issue: GitHub.