Trying to implement reCAPTCHA programmatically in a MERN stack app with typescript, I get the error
recaptchaUtils.ts:9 reCAPTCHA error: Error: Invalid listener argument
at recaptcha__en.js:108:394
at H2 (recaptcha__en.js:609:438)
at Object.ready (recaptcha__en.js:389:417)
at executeRecaptcha (recaptchaUtils.ts:6:1)
at AuthContext.tsx:112:1
at new Promise (<anonymous>)
at login (AuthContext.tsx:110:1)
at handleLogin (Login.tsx:51:1)
at onKeyDown (Login.tsx:102:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
when I try to call this function:
// utils/recaptchaUtils.ts
declare const grecaptcha: any;
export const executeRecaptcha = async (action = 'submit') => {
if (!grecaptcha || !process.env.REACT_APP_RECAPTCHA_SITE_KEY) {
console.error("reCAPTCHA library or site key not available");
return;
}
try {
await grecaptcha.ready();
return await grecaptcha.execute(process.env.REACT_APP_RECAPTCHA_SITE_KEY, { action });
} catch (error) {
console.error("reCAPTCHA error:", error);
}
};
As you can see, it reaches the try-catch block, but is not successful in executing it.
I was able to think of 2 potential problems, like the site key being incorrect (which I had double checked to be correct both in dev and build versions), and the second problem being race conditions.
However, I am certain that reCAPTCHA is loaded before my executeRecaptcha
function is called, because it is loaded like so:
// App.tsx
// ...
declare const grecaptcha: any;
function App() {
useEffect(() => {
const script = document.createElement('script');
script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`;
script.async = true;
script.defer = true;
script.onload = () => {
console.log('reCAPTCHA library loaded.');
grecaptcha.ready(() => {
console.log('reCAPTCHA is ready.');
});
};
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
// ...
and I successfully see reCAPTCHA library loaded.
and reCAPTCHA is ready.
in the console.
I tried including reCAPTCHA in index.js head, but the same outcome.
I tried without the await
, but the same outcome.
I tried without unmounting the reCAPTCHA script, but the same outcome.
I tried testing both in dev and production environments, but the same outcome.
I tried calling executeRecaptcha()
from different components, but the same outcome.
I tried reading the reCAPTCHA docs, but did not find anything useful.
I have fixed the error by changing my executeRecaptcha
function to
export const executeRecaptcha = async (action = 'submit'): Promise<string> => {
return new Promise<string>((resolve, reject) => {
grecaptcha.ready(() => {
try {
grecaptcha.execute(process.env.REACT_APP_RECAPTCHA_SITE_KEY, { action })
.then((token: string) => {
resolve(token);
})
.catch((error: any) => {
console.error("reCAPTCHA error:", error);
reject(error);
});
} catch (error) {
console.error("reCAPTCHA error:", error);
reject(error);
}
});
});
};