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
- If status is 401, prompt the user to re-enter correct email/password (do not auto-retry).
- Confirm local password auth is enabled on the server (check AuthMethodsResponse.local_auth_enabled from getAuthMethods before showing the form).
- Verify VITE_API_BASE_URL points at the server that serves /v1/auth/local/login.
- If 429, wait for the rate-limit window before allowing another attempt.
- Check server logs for /v1/auth/local/login on 5xx statuses and retry once transient errors clear.
- 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
- Show clear 'invalid credentials' messaging for 401 instead of a generic error.
- Check local_auth_enabled via getAuthMethods before rendering the password form.
- Validate email/password fields client-side before submitting.
- Respect rate limits: throttle submit attempts and disable the button while pending.
- Verify VITE_API_BASE_URL per environment and alert on 5xx for the login endpoint.
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
- OAuth init failed (${res.status})
- Auth methods lookup failed (${res.status})
- OAuth redeem failed (${res.status})
- Host returned HTTP ${response.status}
- Invitation not found (${res.status})
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/6999f1117990d761.
Report an issue: GitHub.