BloopAI/vibe-kanban · error · Error

Local login failed (${res.status})

Error message

Local login failed (${res.status})

What it means

localLogin authenticates a user with email and password by POSTing to ${API_BASE}/v1/auth/local/login and expects an access_token/refresh_token pair in the response. If the server responds with any non-OK status it throws 'Local login failed (<status>)'. Most commonly this is a 401 for bad credentials, but any HTTP failure (403, 404, 429, 5xx) produces this error.

Source

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

    }),
  });
  if (!res.ok) {
    throw new Error(`OAuth redeem failed (${res.status})`);
  }
  return res.json();
}

export async function localLogin(
  email: string,
  password: string,
): Promise<LocalLoginResponse> {
  const res = await fetch(`${API_BASE}/v1/auth/local/login`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
  });
  if (!res.ok) {
    throw new Error(`Local login failed (${res.status})`);
  }
  return res.json();
}

export async function getInvitation(
  token: string,
): Promise<InvitationLookupResponse> {
  const res = await fetch(`${API_BASE}/v1/invitations/${token}`);
  if (!res.ok) {
    throw new Error(`Invitation not found (${res.status})`);
  }
  return res.json();
}

export async function acceptInvitation(
  token: string,
  accessToken: string,
): Promise<AcceptInvitationResponse> {

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. If status is 401, prompt the user to re-enter correct email/password (do not auto-retry).
  2. Confirm local password auth is enabled on the server (check AuthMethodsResponse.local_auth_enabled from getAuthMethods before showing the form).
  3. Verify VITE_API_BASE_URL points at the server that serves /v1/auth/local/login.
  4. If 429, wait for the rate-limit window before allowing another attempt.
  5. Check server logs for /v1/auth/local/login on 5xx statuses and retry once transient errors clear.
  6. Validate inputs client-side (non-empty email/password, email format) before calling.

Example fix

// before
await localLogin(email, password);

// after: guard against disabled local auth and handle 401
const methods = await getAuthMethods();
if (!methods.local_auth_enabled) throw new Error('Local login is disabled; use OAuth');
try {
  await localLogin(email.trim(), password);
} catch (e) {
  if (/\(401\)/.test(e.message)) showInvalidCredentials();
  else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate before calling localLogin
const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (!emailOk || password.length === 0) {
  throw new Error('Enter a valid email and password');
}
// optionally ensure local auth is enabled
const methods = await getAuthMethods();
if (!methods.local_auth_enabled) throw new Error('Local login is disabled on this server');

Type guard

function isLocalLoginResponse(x: unknown): x is { access_token: string; refresh_token: string } {
  return typeof x === 'object' && x !== null
    && typeof (x as any).access_token === 'string'
    && typeof (x as any).refresh_token === 'string';
}

Try / catch

try {
  const tokens = await localLogin(email, password);
  saveTokens(tokens);
} catch (e) {
  const status = (e as Error).message.match(/\((\d+)\)/)?.[1];
  if (status === '401') showFormError('Incorrect email or password.');
  else if (status === '429') showFormError('Too many attempts; try again shortly.');
  else showFormError('Login is temporarily unavailable. Please retry.');
}

Prevention

When it happens

Trigger: Calling localLogin(email, password) and the POST /v1/auth/local/login returns non-2xx: wrong email or password (401), local (password) auth disabled on the server (403/404), account locked or rate-limited after repeated attempts (429), wrong API_BASE so the route 404s, or a server error (500/502/503).

Common situations: User mistypes credentials on the login form; deployment has local_auth_enabled=false so the local login endpoint rejects or does not exist; brute-force protection throttles a user retrying many times; VITE_API_BASE_URL misconfigured in self-hosted setups; backend down during deploy.

Related errors


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