BloopAI/vibe-kanban · error · Error

Failed to fetch identity (${res.status})

Error message

Failed to fetch identity (${res.status})

What it means

getIdentity fetches GET {API_BASE}/v1/identity via authenticatedFetch to retrieve the current user's id/username/email. Non-ok responses after the built-in single 401-refresh retry throw this Error. It is commonly surfaced through TanStack-style queries (identityQuery), where it appears as the query's error.

Source

Thrown at packages/remote-web/src/shared/lib/api.ts:207

      method: "POST",
    });
  } finally {
    await clearTokens();
  }
}

export async function listOrganizations(): Promise<ListOrganizationsResponse> {
  const res = await authenticatedFetch(`${API_BASE}/v1/organizations`);
  if (!res.ok) {
    throw new Error(`Failed to list organizations (${res.status})`);
  }
  return res.json();
}

export async function getIdentity(): Promise<IdentityResponse> {
  const res = await authenticatedFetch(`${API_BASE}/v1/identity`);
  if (!res.ok) {
    throw new Error(`Failed to fetch identity (${res.status})`);
  }
  return res.json();
}

export async function listOrganizationProjects(
  organizationId: string,
): Promise<Project[]> {
  const params = new URLSearchParams({
    organization_id: organizationId,
  });

  const res = await authenticatedFetch(`${API_BASE}/v1/projects?${params}`);
  if (!res.ok) {
    throw new Error(`Failed to list projects (${res.status})`);
  }

  const body = (await res.json()) as { projects: Project[] };
  return body.projects;

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Redirect the user to login: clear stored tokens and re-authenticate.
  2. Check status: 401 after retry = tokens unusable (re-login); 403 = account access issue; 5xx = backend problem, check server logs.
  3. Verify VITE_API_BASE_URL points at the correct remote API.
  4. In the consuming query, handle the error state gracefully instead of crashing the identity screen.

Example fix

// before
if (!res.ok) {
  throw new Error(`Failed to fetch identity (${res.status})`);
}
// after
if (!res.ok) {
  const err = new Error(`Failed to fetch identity (${res.status})`);
  (err as Error & { status: number }).status = res.status;
  if (res.status === 401) await clearTokens();
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const accessToken = await getToken();
if (!accessToken) {
  redirectToLogin();
}

Type guard

function isIdentityResponse(v: unknown): v is IdentityResponse {
  const r = v as IdentityResponse;
  return !!r && typeof r.user_id === 'string' && typeof r.email === 'string';
}

Try / catch

const identityQuery = useQuery({
  queryKey: ['identity'],
  queryFn: getIdentity,
  retry: (count, err) => {
    const status = Number(/\((\d{3})\)$/.exec((err as Error).message)?.[1]);
    return status >= 500 && count < 2; // never retry auth failures
  },
});
if (identityQuery.error) {
  const msg = (identityQuery.error as Error).message;
  if (msg.includes('(401)')) redirectToLogin();
}

Prevention

When it happens

Trigger: GET /v1/identity returns 401 after the automatic refresh also failed (refresh token expired/revoked), 403 (account disabled), or 5xx from the remote server. Also when the API base URL is wrong and the endpoint doesn't exist.

Common situations: App restored with stale tokens after server-side token revocation or a server database reset; backend redeployed/invalidating sessions; user account removed while a tab kept old tokens.

Related errors


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