VVibeFix

Guide

AuthApiError: Email not confirmed in Supabase: why new users can't sign in, and how to fix it

A new person signs up, tries to sign in, and your app shows "Email not confirmed". In code it comes back as an AuthApiError with the code email_not_confirmed. Public reports show the raw response as {"code":"email_not_confirmed","message":"Email not confirmed"} (Supabase GitHub issue #29632). The account exists, but the person is locked out of it.

Can a stranger actually sign up and get your email? Find out which of these is breaking your app, usually in 4 to 10 minutes.

Free. No account needed. It usually takes 4 to 10 minutes.

What the error means

Supabase's error code reference describes email_not_confirmed as: "Signing in is not allowed for this user as the email address is not confirmed." The project is doing what it was told. The Confirm email setting means users "will need to confirm their email address before signing in for the first time", and on hosted Supabase projects it is on by default. On self-hosted projects and in local development it is off by default, which is why an app can behave differently on a laptop than live.

So the question to answer is why this person is still unconfirmed. Supabase also advises identifying errors by error.code, not by matching the message text, so check for email_not_confirmed in your sign-in code.

The causes, and how to fix each one

Work through them in this order.

1. The confirmation email was never sent or never arrived

If the email did not leave Supabase, or was blocked after it left, the person has nothing to click. Supabase's default email service only delivers to addresses on your project's team, at 2 messages per hour, and "is not meant for production use".

How to fix it

  1. Open your project's Auth logs in the Supabase dashboard and look for an error at the time of the sign-up. Supabase names this as the first step for undelivered auth emails.
  2. If you are on the default email service, set up your own SMTP provider: get the host, port, username, password and sender address from your provider, then enable custom SMTP in the Authentication settings of the dashboard. Supabase lists Resend, AWS SES, Postmark, Twilio SendGrid, ZeptoMail and Brevo as examples.
  3. With your email provider, set up SPF, DKIM and DMARC for the sending domain. Supabase recommends this for deliverability.
  4. If the logs show an error, see Supabase "Error sending confirmation email". If they show "email rate limit exceeded", see that guide.

Source: Supabase: Send emails with custom SMTP, Supabase troubleshooting: Not receiving Auth emails from the Supabase project

2. The email arrived, but the link was already used up

Supabase documents that some email providers' security features prefetch links in incoming emails. The one-time link is consumed before the person clicks it, and they then see "Token has expired or is invalid" and stay unconfirmed.

How to fix it

  1. Open the Email Templates page in the Supabase dashboard and edit the Confirm signup template.
  2. Choose one of Supabase's two documented options: send a one-time code by putting {{ .Token }} in the template and verify it in your app with supabase.auth.verifyOtp, or link to a page in your app with a button that opens the real confirmation link.
  3. Send the affected person a new confirmation email (cause 4 shows how).

Source: Supabase: Email templates

3. The link sent them to localhost or a page that does not finish the job

Supabase's Site URL defaults to http://localhost:3000, and it is used whenever the app does not name an approved redirect. With the PKCE flow, Supabase's own template sends the person to {{ .SiteURL }}/auth/confirm?token_hash=..., so the confirmation only completes if that page exists on your live app.

How to fix it

  1. Open the URL Configuration page in the Supabase dashboard and change the Site URL from localhost to your live address.
  2. Add your live address to the Redirect URLs list, for example https://yourapp.com/**.
  3. In the sign-up call, pass options: { emailRedirectTo: 'https://yourapp.com/welcome' }. Supabase says the address must be on the Redirect URLs list.
  4. If your template uses a token-hash link, make sure your live app has the page it points to. The full walk-through is in Supabase redirect URL localhost.

Source: Supabase: Redirect URLs, Supabase: Password-based auth

4. They tried to sign in again, expecting a new email

Signing in does not send a new confirmation email. Someone who keeps trying to sign in only gets the same refusal. Lovable's documentation says it plainly: "signing in again does not resend it".

How to fix it

  1. Catch email_not_confirmed in your sign-in screen and show a "Resend confirmation email" button instead of a generic error.
  2. Have the button call supabase.auth.resend({ type: 'signup', email, options: { emailRedirectTo } }), which Supabase documents for resending a signup confirmation.
  3. Disable the button for at least 60 seconds after each send. Supabase's default allows one signup confirmation request per user per 60 seconds.
  4. On an app on Lovable's built-in backend without such a button, Lovable's documented route is for the person to sign up again with the same address.

Source: Supabase JavaScript reference: resend(), Supabase: Rate limits, Lovable: Email authentication for your app

5. The account was created outside the normal sign-up

Users created with the admin API (auth.admin.createUser) without email_confirm: true can be left unconfirmed, and they then hit the same error. Public reports describe this even after Confirm email was switched off (#29632).

How to fix it

  1. Confirm the account on the server with supabase.auth.admin.updateUserById(id, { email_confirm: true }). Supabase says this function "should only be called on a server" and never with the service_role key in the browser.
  2. On Lovable's built-in backend, accounts you create yourself under More, then Users, then Add user and Create new user are confirmed automatically.

Source: Supabase JavaScript reference: auth.admin.updateUserById(), Lovable: Email authentication for your app

6. The app assumes people are signed in right after sign-up

Confirmation is one switch for the whole project. If your screens send a new person straight into the app after sign-up, the next sign-in attempt meets this error.

How to fix it

  1. Decide whether you want confirmation. Supabase keeps it on by default for hosted projects. In Lovable the matching setting is Auto-confirm email under More, then Users, then Settings, then Email, and Lovable recommends keeping confirmation on for live apps.
  2. If confirmation stays on, show a "check your email" screen after sign-up instead of opening the app, and explain that the link must be clicked before signing in.
  3. Test the full path with a brand-new address, not your own account.

Source: Supabase: General configuration, Supabase: Password-based auth, Lovable: Email authentication for your app

Your AI reads your code. VibeFix signs up like a stranger. Find out which of these is breaking your app, usually in 4 to 10 minutes.

Free. No account needed. It usually takes 4 to 10 minutes.

Why you cannot see it yourself

You're signed in, your customers aren't. You confirmed your own account long ago, from an address that is on the team. You never sit where a new person sits: signed up, unconfirmed, waiting for an email that may never come.

Frequently asked questions

I switched off "Confirm email", but users still get "Email not confirmed". Why?

People report this on Supabase's GitHub, for example #29632 (users created with the admin client) and #5113. The dependable way out for an account that is already stuck is to confirm that account: send a new confirmation email with resend(), or confirm it on the server with updateUserById(id, { email_confirm: true }).

How do I resend the confirmation email?

Use supabase.auth.resend({ type: 'signup', email }). This was a long-standing question in Supabase discussion #3526, and Supabase now documents resend() for it. Keep the button disabled for about 60 seconds: Supabase's per-user 60-second window applies to the sign-up request, and resend requests are also limited per IP address.

Can people use the app before confirming their email?

Only by switching confirmation off for the whole project (Confirm email in Supabase, Auto-confirm email in Lovable). A request for a middle ground (supabase/auth #1494) was closed in 2024, with anonymous sign-in suggested as the alternative. Switching it off means anyone can sign up with an address they do not own.

Related guides

Sources (official documentation)

Fixed it? Prove a real stranger gets in

After the fix, the test that matters is a brand-new address signing up, getting the email, clicking the link and signing in on your live app. Your own account cannot show that, because it is not new. Your AI reads your code. VibeFix signs up like a stranger.

Free check. An AI agent signs up to your live app as a brand-new person, with a real inbox of its own, on a phone and on a computer. You get the problem count, what each problem is, and one screenshot. It never changes your app and never emails your users. Test my app, free

$9 for one app. Shows which cause is breaking your app and where, the exact fix prompt for your app for every problem, plus 3 re-checks so you can see whether the fix holds for a stranger. It does not promise a fix; it shows what the agent saw.

VibeFix is independent. Supabase and Lovable are trademarks of their owners.

More guides