Supabase otp_expired: OTP code for this sign-in has expired

Supabase returns otp_expired on a magic link or email code? That code has expired. Supabase says to sign in again. The default lifetime is 1 hour.

Reviewed October 21, 2026

What it means in plain English

Supabase Auth's error code otp_expired means the OTP code for this sign-in has expired. The error-codes guide says to ask the user to sign in again, and to branch on error.code rather than matching the message text.

Email magic links and email one-time passwords share that expiry. Supabase's passwordless guide says a user can request one once every 60 seconds, and they expire after 1 hour by default. The same Email OTP expiration setting also governs magic links and the other email links: confirmation, password recovery, email change, and invitation.

Likely causes

  • The person opened the link or entered the code after that expiry.
  • Email OTP expiration is still the default 1 hour, and the inbox was checked later than that.
  • The request is a different limit. over_email_send_rate_limit means too many emails were sent to this address. over_request_rate_limit means too many requests from this client.
  • The error is not about expiry at all. A magic-link or OTP request for an email with no account, with shouldCreateUser set to false, fails with Signups not allowed for otp.

How to fix it, step by step

  1. Read error.code. Treat otp_expired as an expired code. Ask the person to request a new sign-in.
  2. Open Authentication, then Sign In / Providers, then Auth Providers, then Email, then Email OTP expiration. Supabase says that is where the duration is set. A value above 86,400 seconds (one day) is strongly discouraged and can only be set with the Management API.
  3. Leave the 60-second request spacing in place. Sending the same address another code inside that window is a different failure.
  4. If the new link opens localhost, the Site URL is a separate setting. See Supabase login redirects to localhost:3000 after deploy.
  5. If the code is over_email_send_rate_limit, wait before sending another email to that address. See email rate limit exceeded (Supabase Auth).

How to check it worked

  • A new magic link or email code, used inside the expiry you set, returns a session.
  • error.code on a late attempt is otp_expired, and a fresh request signs the person in.

Sources

More Supabase auth and email fixes

Keep going on your own.

The guides stay free. A deep leak report is $15. Fix packs are $24, or $49 for all three. There is no done-for-you repair.

See products