antiwork/gumroad · error · ResponseError
Could not add this passkey. Please try again.
Error message
Could not add this passkey. Please try again.
What it means
First step of passkey registration: the client POSTs to registration_options_settings_passkeys_path to get WebAuthn PublicKeyCredentialCreationOptions. The throw fires when the response is not ok, the parsed body has success:false, or the options field is missing; the message is the server's error_message when present, otherwise the PASSKEY_ADD_ERROR fallback ('Could not add this passkey. Please try again.'). typia.assert can also throw earlier if the body does not match the declared shape. Note that 5xx and 429 never reach this line — request() already converts them to ResponseError/RateLimitError.
Source
Thrown at app/javascript/utils/passkeyRegistration.ts:27
created_at: string;
last_used_at: string | null;
};
export const PASSKEY_ADD_ERROR = "Could not add this passkey. Please try again.";
export const registerPasskey = async (): Promise<Passkey> => {
const optionsResponse = await request({
url: Routes.registration_options_settings_passkeys_path(),
method: "POST",
accept: "json",
});
const optionsResult = typia.assert<{
success: boolean;
options?: PasskeyRegistrationOptions;
error_message?: string;
}>(await optionsResponse.json());
if (!optionsResponse.ok || !optionsResult.success || !optionsResult.options) {
throw new ResponseError(optionsResult.error_message ?? PASSKEY_ADD_ERROR);
}
const credential = await createPasskey(optionsResult.options);
const createResponse = await request({
url: Routes.settings_passkeys_path(),
method: "POST",
accept: "json",
data: { credential },
});
const createResult = typia.assert<{ success: boolean; passkey?: Passkey; error_message?: string }>(
await createResponse.json(),
);
if (!createResponse.ok || !createResult.success || !createResult.passkey) {
throw new ResponseError(createResult.error_message ?? PASSKEY_ADD_ERROR);
}
return createResult.passkey;View on GitHub (pinned to afeacbd394)
Solutions
- Check the network response for the registration options POST — the status and body distinguish auth failure from server-side config failure.
- If 401/CSRF, refresh the page to pick up a fresh session and CSRF token, then retry.
- If success:false with error_message, read it — it is the server's own explanation (config problem, limit reached).
- Check server logs for the options-building code path (WebAuthn config, RP ID/origin).
- Verify the Rails webauthn initializer: RP ID must match the serving origin.
Example fix
// before
if (!optionsResponse.ok || !optionsResult.success || !optionsResult.options) {
throw new ResponseError(optionsResult.error_message ?? PASSKEY_ADD_ERROR);
}
// after — keep the server's reason, add the status for diagnosis when it stayed silent
if (!optionsResponse.ok || !optionsResult.success || !optionsResult.options) {
throw new ResponseError(
optionsResult.error_message ?? `Could not add this passkey (HTTP ${optionsResponse.status}). Please try again.`,
);
} Defensive patterns
Strategy: try-catch
Type guard
const isResponseError = (e: unknown): e is ResponseError => e instanceof ResponseError;
Try / catch
try {
await registerPasskey();
} catch (e) {
if (e instanceof DOMException && e.name === 'NotAllowedError') return; // user cancelled the browser prompt — not an error
if (e instanceof RateLimitError) { showAlert(e.message, 'error'); return; }
assertResponseError(e);
showAlert(e.message, 'error'); // carries the server's error_message
} Prevention
- Trigger registration from a fresh page load so the session and CSRF token are current.
- Keep WebAuthn RP ID / origins configured for every host the app is served from (staging included).
- Handle NotAllowedError (user dismissed the browser dialog) separately — showing 'could not add' for a cancellation confuses users.
- RateLimitError extends ResponseError; check it first so a 429 keeps the server's wait-time wording.
When it happens
Trigger: POST to the registration options endpoint returning 401 (session or CSRF token invalid/expired), success:false with an error_message (e.g. server-side WebAuthn config broken, user hit a passkey limit), a missing options payload, or a non-JSON body that fails typia.assert.
Common situations: Seller leaves the Settings page open past session expiry and clicks 'Add passkey'; WebAuthn relying party ID/origin env vars misconfigured in Rails so the server cannot build options; a proxy returning an HTML error page where JSON is expected.
Related errors
- Sorry, something went wrong. Please try again.
- We couldn't sign you in with that passkey. Please try again
- Sorry, something went wrong. Please try again.
- Something went wrong.
- Something went wrong.
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/8620c2c4d067f32e.
Report an issue: GitHub.