antiwork/gumroad · error · ResponseError

We couldn't sign you in with that passkey. Please try again

Error message

We couldn't sign you in with that passkey. Please try again or use your password.

What it means

Fallback shown when the first step of passkey sign-in on /login fails: POSTing to Routes.login_passkey_options_path to fetch WebAuthn authentication options. It throws ResponseError with the server's error_message when !optionsResponse.ok, success is false, or options is missing — or when typia.assert rejects a malformed body. The catch deliberately swallows AbortError and browser DOMException NotAllowedError/AbortError, so user cancellations of the native passkey sheet never surface this message; only genuine fetch/option failures set the visible passkey error.

Source

Thrown at app/javascript/pages/Logins/New.tsx:106

      signal?: AbortSignal;
      surfaceErrors: boolean;
    }) => {
      try {
        let options = embeddedOptions;
        if (!options) {
          const optionsResponse = await request({
            url: Routes.login_passkey_options_path(),
            method: "POST",
            accept: "json",
            abortSignal: signal,
          });
          const optionsResult = typia.assert<{
            success: boolean;
            options?: PasskeyAuthenticationOptions;
            error_message?: string;
          }>(await optionsResponse.json());
          if (!optionsResponse.ok || !optionsResult.success || !optionsResult.options) {
            throw new ResponseError(optionsResult.error_message ?? PASSKEY_ERROR);
          }
          options = optionsResult.options;
        }

        const credential = await getPasskey(options, { mediation, signal });

        setPasskeyLoading(true);

        const loginResponse = await request({
          url: Routes.login_passkey_path(),
          method: "POST",
          accept: "json",
          data: { credential, next },
          abortSignal: signal,
        });
        const loginResult = typia.assert<{ success: boolean; redirect_location?: string; error_message?: string }>(
          await loginResponse.json(),
        );

View on GitHub (pinned to afeacbd394)

Solutions

  1. Retry the passkey button — transient option-fetch failures are common.
  2. If it persists, use password sign-in (the message's own fallback).
  3. Devtools: check the passkey_options POST status and body — a server error_message would have been shown verbatim, so this fallback means the body had none.
  4. Maintainers: keep the { success, options, error_message } shape stable; typia violations surface as this same generic fallback.
Defensive patterns

Strategy: try-catch

Type guard

const isOptionsFetchFailure = (
  response: Response,
  result: { success?: boolean; options?: PasskeyAuthenticationOptions },
): boolean => !response.ok || result.success !== true || result.options == null;

Try / catch

try {
  const optionsResponse = await request({ url: Routes.login_passkey_options_path(), method: "POST", accept: "json", abortSignal: signal });
  const optionsResult = typia.assert<{ success: boolean; options?: PasskeyAuthenticationOptions; error_message?: string }>(await optionsResponse.json());
  if (!optionsResponse.ok || !optionsResult.success || !optionsResult.options) {
    throw new ResponseError(optionsResult.error_message ?? PASSKEY_ERROR);
  }
} catch (e) {
  if (!signal?.aborted) setLoading(false);
  if (e instanceof AbortError) return;
  if (e instanceof DOMException && (e.name === "NotAllowedError" || e.name === "AbortError")) return; // user dismissed the native sheet
  if (surfaceErrors) setPasskeyError(e instanceof ResponseError ? e.message : PASSKEY_ERROR);
}

Prevention

When it happens

Trigger: POST /login/passkey_options returns 4xx/5xx (rate limit, CSRF/session problem, server error), success:false (no credentials match), options omitted, or the response body drifts from the asserted { success, options, error_message } shape.

Common situations: Conditional-UI/usernameless autofall kicking off before cookies are ready, Rails route or serialization changes breaking the options contract, aggressive rate limiting on repeated attempts, or users with no registered passkeys pressing the passkey button.

Related errors


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