BloopAI/vibe-kanban · error · Error

Session refresh failed. Please sign in again.

Error message

Session refresh failed. Please sign in again.

What it means

tokenManager throws this when the token refresh call fails with any non-401 status (or a network/unknown error without a status), i.e. the session could not be refreshed but the server did not explicitly say the token is invalid. Tokens are cleared and refreshPromise is reset in finally so a later call can retry cleanly.

Source

Thrown at packages/remote-web/src/shared/lib/auth/tokenManager.ts:84

  if (refreshPromise) return refreshPromise;

  const innerPromise =
    typeof navigator.locks?.request === "function"
      ? navigator.locks
          .request("rf-token-refresh", doTokenRefresh)
          .then((t) => t)
      : doTokenRefresh();

  const promise = innerPromise
    .catch(async (error: unknown) => {
      await clearTokens();

      const status = (error as { status?: number }).status;
      if (status === 401) {
        throw new Error("Session expired. Please sign in again.");
      }

      throw new Error("Session refresh failed. Please sign in again.");
    })
    .finally(() => {
      refreshPromise = null;
    });

  refreshPromise = promise;
  return promise;
}

export async function getToken(): Promise<string> {
  const accessToken = await getAccessToken();
  if (!accessToken) {
    if (!(await getRefreshToken())) throw new Error("Not authenticated");
    return handleTokenRefresh();
  }
  if (shouldRefreshAccessToken(accessToken)) return handleTokenRefresh();
  return accessToken;
}

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Retry the refresh once or twice with backoff for transient 5xx/network failures before clearing tokens.
  2. Check the auth server's health/logs for the corresponding failed request.
  3. Verify the refresh endpoint URL, CORS headers, and proxy configuration.
  4. Redirect to sign-in if retries also fail (tokens are already cleared).

Example fix

// before
const promise = innerPromise.catch(async (error) => { await clearTokens(); throw new Error('Session refresh failed...'); });
// after
const promise = innerPromise.catch(async (error) => {
  const status = (error as { status?: number }).status;
  if (status && status >= 500) { /* schedule a retry instead of clearing tokens */ }
  await clearTokens();
  throw new Error(status === 401 ? 'Session expired...' : 'Session refresh failed...');
});
Defensive patterns

Strategy: retry

Validate before calling

async function isAuthServerReachable(url: string): Promise<boolean> {
  try { const r = await fetch(url, { method: 'HEAD' }); return r.status < 500; }
  catch { return false; }
}

Type guard

function isTransientRefreshError(e: unknown): boolean {
  const status = (e as { status?: number })?.status;
  return status === undefined || status >= 500;
}

Try / catch

try {
  const token = await getToken();
} catch (e) {
  if (isTransientRefreshError(e)) {
    await backoff(() => getToken(), 3);
  } else {
    redirectToLogin({ reason: 'refresh_failed' });
  }
}

Prevention

When it happens

Trigger: getToken()/triggerRefresh() -> handleTokenRefresh; the refresh request rejects or resolves with a status other than 401 (500, 502, 503, timeout, TypeError from fetch) — the catch falls through the 401 check and throws this message.

Common situations: Auth server temporarily down or behind a failing proxy/load balancer; CORS misconfiguration on the refresh endpoint; DNS/network outage on the client; intermittent 5xx during deployments.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/ccc12ab01222c2e2. Report an issue: GitHub.