Source signal: Search Console keyword burst: timeout-or-duplicate, captcha token expired, recaptcha verification failed.
The error is a token lifecycle problem, not a mystery
The reCAPTCHA error code timeout-or-duplicate usually means the token reached the server too late, was verified more than once, or was created for a user action that no longer matches the request being checked. For site owners, the frustrating part is that the widget can look successful in the browser while the server rejects the response. Users see a vague failure and try again, which can turn one stale token into a loop of failed submissions.
This shows up repeatedly in developer discussions: a token is generated on page load, the visitor spends a few minutes filling in a form, then the backend verifies an already-old response. It also appears in multi-step forms where validation runs twice, or in AJAX flows where two buttons reuse the same hidden g-recaptcha-response. The fix is to treat the token as a short-lived, single-use proof for one specific action.
Common implementation patterns that create stale tokens
Do not execute reCAPTCHA v3 on page load for a form that may sit open. Execute it at submit time. If client-side validation fails, request a new token before the next submit. If the server validates the same token in both form cleaning and controller code, remove one of the checks. If the page has two independent actions, generate a separate token for each action and label the action name clearly.
Double-submit bugs are especially common. A user clicks once, JavaScript submits, then the browser submits again because the handler did not prevent the default event. The first request consumes the token. The second request reaches the backend with the same token and receives timeout-or-duplicate. Disable the submit button after the first click and make the server endpoint idempotent enough to avoid punishing legitimate retries.
How rCAPTCHA reduces this failure class
rCAPTCHA should be positioned around clear verification sessions rather than loose hidden fields. A site can bind a session to the current page, user gesture, form, and timestamp, then expire it predictably. Instead of leaving the user to discover a stale token after a full form submit, the integration can refresh the challenge state before submission or return a structured retry reason.
For forms with long completion time, rCAPTCHA can verify closer to the actual protected action and report whether the failure was expired, reused, missing, or suspicious. That is a better operator experience than one generic invalid CAPTCHA banner. It also gives site owners a metric they can act on: token age at failure, duplicate verification count, and which route consumed the token first.
Operational checklist
Generate tokens only when the protected action is ready. Verify each token once. Store a short server-side record of token hashes that have already been consumed. Add client-side protection against duplicate submit. Log the token age, action name, route, user agent, and validation path when verification fails. If failures spike, check frontend deploys, browser extension reports, and whether a CDN or security plugin is replaying form posts.
A clean implementation turns timeout-or-duplicate from a user-facing dead end into an engineering signal. The user gets a fast retry; the site owner gets enough detail to fix the root cause.
Sources and further reading
- Stack Overflow: reCAPTCHA v3 timeout-or-duplicate on repeated submit
- Stack Overflow: validation twice causes timeout-or-duplicate
- Stack Overflow: multiple actions reusing a v3 token
For site owners, the larger lesson is simple: users search for exact failure text because generic CAPTCHA errors do not help them. rCAPTCHA should make each failure measurable, explainable, and recoverable without weakening abuse protection.