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

  1. Check the network response for the registration options POST — the status and body distinguish auth failure from server-side config failure.
  2. If 401/CSRF, refresh the page to pick up a fresh session and CSRF token, then retry.
  3. If success:false with error_message, read it — it is the server's own explanation (config problem, limit reached).
  4. Check server logs for the options-building code path (WebAuthn config, RP ID/origin).
  5. 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

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


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/8620c2c4d067f32e. Report an issue: GitHub.