apps/docs/content/troubleshooting/otp-verification-failures-token-has-expired-or-otp_expired-errors-5ee4d0.mdx
When users attempt to exchange One-Time Passwords (OTPs), they may encounter various errors indicating that the token is no longer valid. These include messages like "token has expired or is invalid," "Email link is invalid or has expired," or they might receive '403 Forbidden' HTTP responses on the verification endpoint. Authentication logs often show specific otp_expired error codes.
An OTP, or One-Time Password, is a security code generated for a single use or a limited period. In authentication flows, especially for password resets, a user requests an OTP, which is then sent to their email or phone. The user must then provide this OTP back to the system within a set timeframe to verify their identity and proceed with actions like resetting a password. The system verifies this token using a function like supabase.auth.verifyOtp({ email, token, type: 'recovery' }).
/verify endpoint typically means the server has rejected the submitted OTP token, often because it's invalid, expired, or used.otp_expired Error: This is a specific error message reported by the authentication service, explicitly stating that the provided OTP token has passed its validity period and can no longer be used. Other similar messages like "token has expired or is invalid" or "Email link is invalid or has expired" also fall under this category of token validity issues.The most common reason for OTP tokens appearing expired or invalid before a user can even use them is email prefetching.
To determine if email prefetching or another token validity issue is the root cause, you can correlate events across your logs:
/verify endpoint. These responses directly indicate failed verification attempts.otp_expired error codes or similar "token invalid/expired" messages.otp_expired messages shortly after an email is sent, but before a user could reasonably click the link, it strongly suggests prefetching.supabase.auth.verifyOtp with the correct type (e.g., recovery for password resets). While it's understood that the 'recovery' type is correct for password resets, the issue primarily stems from the initial link access rather than the final verification call.Addressing this problem typically involves adjusting how your application interacts with email and how tokens are handled:
{{ .ConfirmationURL }}) that, when accessed, might implicitly validate or invalidate an OTP token.rel="noreferrer noopener") or email headers that signal security scanners not to follow links.