dotnet/aspnetcore · error · Error
The server responded with status
Error message
The server responded with status ${response.status}. What it means
Thrown by fetchWithErrorHandling after a non-ok HTTP response from the passkey endpoints (/Account/PasskeyCreationOptions, /Account/PasskeyRequestOptions). The server returned an HTTP error status (4xx/5xx), so the passkey credential flow cannot proceed. The response body is logged to console.error before throwing.
Solutions
- Open DevTools Network tab, inspect the failing /Account/Passkey* request status and response body (already console.error'd) to identify the exact HTTP code.
- If 400 Bad Request: verify the anti-forgery token is being sent (the form must include the request verification token and cookies:'include' must remain in effect).
- If 401/403: confirm the user is still authenticated and the endpoint's [Authorize] policy is satisfied before invoking passkey UI.
- If 404: confirm AddPasskey / MapPasskeyEndpoints is registered in Program.cs and the route matches '/Account/PasskeyCreationOptions'.
- If 500: inspect the server-side exception in ASP.NET Core logs; the passkey options handler likely threw.
Example fix
// before
const response = await fetch(url, { credentials: 'include', ...options });
if (!response.ok) { /* generic throw */ }
// after — surface server-provided error text to the UI
async function fetchWithErrorHandling(url, options = {}) {
const response = await fetch(url, { credentials: 'include', ...options });
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(`Passkey request to ${url} failed (${response.status}): ${text}`);
}
return response;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the endpoint is reachable & authed before offering passkey UI
async function canReachPasskeyEndpoint() {
try {
const r = await fetch('/Account/PasskeyCreationOptions', {
method: 'HEAD', credentials: 'include'
});
return r.ok || r.status === 405; // 405 = endpoint exists, HEAD not allowed
} catch { return false; }
} Try / catch
try {
await component.obtainAndSubmitCredential();
} catch (e) {
if (/server responded with status (\d+)/.test(e.message)) {
const status = RegExp.$1;
showUserError(status === 401 ? 'Session expired — please sign in again.' : 'Passkey request failed. Try a different sign-in method.');
} else throw e;
} Prevention
- Ensure credentials:'include' is not overridden by callers spreading options.
- Keep the anti-forgery token present in the form before triggering passkey submit.
- Render a fallback login method so a server error is recoverable for the user.
When it happens
Trigger: Calling fetchWithErrorHandling against /Account/PasskeyCreationOptions or /Account/PasskeyRequestOptions when the ASP.NET Core account endpoint returns non-2xx. Anti-forgery token failure (400), missing authentication (401), server exception (500), or endpoint not mapped (404) all hit this path.
Common situations: Anti-forgery cookie/token not sent because credentials:'include' is overridden by a same-origin policy change; the Blazor account endpoints were customized or removed; the user session expired mid-flow; ASP.NET Core rate limiting or authorization policies reject the request.
Related errors
- Could not load settings from
- errorMessage || response.statusText
- Authorization requires a cascading parameter of type Task
- Failed to download , Status Code
- Failed to retrieve token, no account found.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/d7b71564ad2bb13a.
Report an issue: GitHub.
Appendix: source
Thrown at src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Shared/PasskeySubmit.razor.js:15
const browserSupportsPasskeys =
typeof navigator.credentials !== 'undefined' &&
typeof window.PublicKeyCredential !== 'undefined' &&
typeof window.PublicKeyCredential.parseCreationOptionsFromJSON === 'function' &&
typeof window.PublicKeyCredential.parseRequestOptionsFromJSON === 'function';
async function fetchWithErrorHandling(url, options = {}) {
const response = await fetch(url, {
credentials: 'include',
...options
});
if (!response.ok) {
const text = await response.text();
console.error(text);
throw new Error(`The server responded with status ${response.status}.`);
}
return response;
}
async function createCredential(signal) {
const optionsResponse = await fetchWithErrorHandling('/Account/PasskeyCreationOptions', {
method: 'POST',
signal,
});
const optionsJson = await optionsResponse.json();
const options = PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
return await navigator.credentials.create({ publicKey: options, signal });
}
async function requestCredential(email, mediation, signal) {
const optionsResponse = await fetchWithErrorHandling(`/Account/PasskeyRequestOptions?username=${email}`, {
method: 'POST',
signal,View on GitHub (pinned to 3600ca084e)