BloopAI/vibe-kanban · error
Session expired. Please log in again.
Error message
Session expired. Please log in again.
What it means
makeAuthenticatedRequest in remoteApi.ts throws this when a request returns HTTP 401 and the token refresh (authRuntime.triggerRefresh()) fails to produce a new token, so the request is not retried. It means the user's session cannot be silently restored.
Source
Thrown at packages/web-core/src/shared/lib/remoteApi.ts:94
...options,
headers,
credentials: 'include',
});
// Handle 401 - token may have expired
if (response.status === 401 && retryOn401) {
const newToken = await authRuntime.triggerRefresh();
if (newToken) {
// Retry the request with the new token
headers.set('Authorization', `Bearer ${newToken}`);
return fetch(`${baseUrl}${path}`, {
...options,
headers,
credentials: 'include',
});
}
// Refresh failed, throw an auth error
throw new Error('Session expired. Please log in again.');
}
return response;
}
export interface BulkUpdateIssueItem {
id: string;
changes: Partial<UpdateIssueRequest>;
}
export interface BulkUpdateProjectItem {
id: string;
changes: Partial<UpdateProjectRequest>;
}
export async function bulkUpdateProjects(
updates: BulkUpdateProjectItem[]
): Promise<void> {View on GitHub (pinned to 4deb7eca8f)
Solutions
- Catch the error and route the user to login (the message is user-facing).
- Verify the auth runtime's refresh endpoint URL and that credentials:'include' cookies reach it (SameSite/cORS settings).
- Check server logs for the refresh rejection reason (expired vs revoked vs invalid client version).
- Clear local auth state before redirecting so the fresh login doesn't collide with stale tokens.
Example fix
// before
const resp = await makeRequest('/v1/projects');
// after
try {
const resp = await makeRequest('/v1/projects');
} catch (e) {
if (e instanceof Error && e.message.includes('Session expired')) {
await authRuntime.logout();
window.location.assign('/login');
} else {
throw e;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
const authRuntime = getAuthRuntime(); if (!(await authRuntime.getToken())) redirectToLogin();
Type guard
function isSessionExpiredError(e: unknown): e is Error {
return e instanceof Error && e.message.includes('Session expired');
} Try / catch
try {
const resp = await makeRequest('/v1/projects');
} catch (e) {
if (isSessionExpiredError(e)) {
await authRuntime.logout();
window.location.assign('/login?reason=session-expired');
} else { throw e; }
} Prevention
- Schedule proactive token refresh before the access token expires
- Verify the refresh endpoint and its cookies work with credentials:'include' (SameSite/CORS)
- Clear stale tokens before redirecting to a fresh login
- Avoid retry loops: this error is terminal — always re-authenticate interactively
When it happens
Trigger: Any makeRequest-based remote API call gets 401; triggerRefresh() returns null because the refresh token is expired/absent or the refresh endpoint rejects it. Second 401 after refresh does NOT rethrow this (retryOn401 default true only retries once; a failed retry response is returned as-is).
Common situations: Long-lived tab whose refresh token expired; refresh cookie missing due to cookie policy or cross-site fetch; the shared API base URL changed (setRemoteApiBase) so the refresh endpoint differs; server-side session revocation.
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/fe795844ca4e63d7.
Report an issue: GitHub.