antiwork/gumroad · error · RecaptchaUnavailableError

We couldn't load the security check. This is often caused by

Error message

We couldn't load the security check. This is often caused by an ad blocker or privacy extension — try disabling it for this page, using a private/incognito window, or switching networks, then try again.

What it means

RecaptchaUnavailableError thrown from useRecaptcha's score-based (invisible) branch when the Google reCAPTCHA Enterprise script cannot be loaded — loadRecaptchaScript(scoreScriptUrl(siteKey)) rejects — or when grecaptcha.enterprise.execute() fails to produce a token. The class itself carries no message; callers (Checkout PaymentForm, follow forms) render the shared RECAPTCHA_UNAVAILABLE_MESSAGE guidance when they catch it. It is deliberately distinct from RecaptchaCancelledError (user dismissed a challenge): this error is environmental — an extension or network is blocking Google.

Source

Thrown at app/javascript/components/useRecaptcha.tsx:145

        });
      });

    const initPromise = loadRecaptchaScript(CHALLENGE_SCRIPT_URL).then(initRecaptcha);
    initPromiseRef.current = initPromise;
    // Swallow the rejection here so a blocked script doesn't surface as an unhandled promise
    // rejection — execute() re-checks the stored promise and reports the failure to the user.
    initPromise.catch(() => {});
  }, [siteKey, scoreBased]);

  const execute = () => {
    if (!siteKey) return Promise.reject(new RecaptchaCancelledError());

    if (scoreBased) {
      return loadRecaptchaScript(scoreScriptUrl(siteKey))
        .catch(() => {
          // The script failing to load is environmental (blocked by an extension or the
          // network), not a user dismissal — surface it as such so the UI can show guidance.
          throw new RecaptchaUnavailableError();
        })
        .then(
          () =>
            new Promise<string>((resolve, reject) => {
              grecaptcha.enterprise.ready(() => {
                grecaptcha.enterprise.execute(siteKey, { action }).then(resolve, () => {
                  // Score keys never show a challenge, so there is nothing for the user to
                  // dismiss — a token failure here is also environmental.
                  reject(new RecaptchaUnavailableError());
                });
              });
            }),
        );
    }

    // Wait for initialization to finish before checking the widget: a submit that lands
    // before the script has loaded and rendered the widget is a normal timing race, not a
    // blocked CAPTCHA, so we must not report it as unavailable prematurely.

View on GitHub (pinned to afeacbd394)

Solutions

  1. Disable the ad blocker/privacy extension for the page or use a private/incognito window — exactly what the message suggests.
  2. Verify the page CSP allows script-src https://www.google.com (plus frame-src/connect-src for the widget flow).
  3. Confirm the reCAPTCHA Enterprise site key's allowed domains include the current domain.
  4. Test from another network to rule out filtered connectivity to google.com.
Defensive patterns

Strategy: fallback

Type guard

import { RecaptchaCancelledError, RecaptchaUnavailableError } from "$app/components/useRecaptcha";

const isRecaptchaUnavailable = (e: unknown): e is RecaptchaUnavailableError => e instanceof RecaptchaUnavailableError;
const isRecaptchaCancelled = (e: unknown): e is RecaptchaCancelledError => e instanceof RecaptchaCancelledError;

Try / catch

try {
  const token = await execute();
} catch (e) {
  if (isRecaptchaCancelled(e)) return; // user dismissed; retry quietly
  if (isRecaptchaUnavailable(e)) {
    showAlert(RECAPTCHA_UNAVAILABLE_MESSAGE, "error"); // actionable guidance
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Loading https://www.google.com/recaptcha/enterprise.js?render=<siteKey> fails: an ad blocker/privacy extension blocks it, CSP script-src doesn't allow www.google.com, the network is offline or filters Google (e.g. mainland China), or enterprise.execute() rejects (site key not valid for the domain).

Common situations: Checkout or follow-button submissions failing for a subset of buyers running uBlock/Privacy Badger; local dev with restrictive CSP; staging domains not added to the reCAPTCHA key's allowed-domain list; corporate proxies blocking third-party scripts.

Related errors


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