How to fix email_exists: Email address already exists in Supabase

SupabaseINTERMEDIATEMEDIUMUpdated Dec 22, 2025· 333 views

This error occurs when attempting to sign up a user with an email address already registered in Supabase Auth. The behavior depends on your email confirmation settings.

Quick fix

Fixes email_exists: Email address already exists

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'password123'
});

if (error?.message === 'email_exists') {
  // Email confirmation is disabled - direct error
  console.log('Email already registered - redirect to login');
  return;
}

if (data?.user) {
  // Email confirmation enabled - check identities array
  if (data.user.identities && data.user.identities.length === 0) {
    console.log('Email already exists - redirect to login');
    return;
  }

  // New user created successfully
  console.log('Signup successful - check email for verification');
}
  1. Check your email confirmation settings
  2. Detect existing emails by checking the identities array
  3. Use Admin Auth for server-side email checks
  4. Create a custom RPC function to check email existence
supabase — error
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'password123'
});
if (error?.message === 'email_exists') {
// Email confirmation is disabled - direct error
console.log('Email already registered - redirect to login');
return;
}
if (data?.user) {
// Email confirmation enabled - check identities array
if (data.user.identities && data.user.identities.length === 0) {
console.log('Email already exists - redirect to login');
return;
}
// New user created successfully
console.log('Signup successful - check email for verification');
}
email_exists: Email address already exists

What this error means

The "email_exists" error is thrown by Supabase Auth when the email provided during sign-up is already associated with an existing user account in the auth.users table. Supabase enforces email uniqueness to maintain data integrity and prevent duplicate accounts. The error's behavior depends on your project's email confirmation settings. When email confirmation is disabled, Supabase returns a clear "email_exists" error message. However, when email confirmation is enabled, Supabase returns an obfuscated/fake user object instead of a direct error message. This is a deliberate security design decision to prevent account enumeration attacks, where attackers could determine which email addresses are registered by attempting signups. This security-focused approach means you need to detect existing users differently depending on your authentication configuration. The workarounds include checking the identities array, using Admin Auth for server-side checks, or implementing custom email existence verification.

Typical symptoms

  • signUp() call returns an error with message "email_exists" when email confirmation is disabled
  • signUp() returns a success response with empty identities array when email confirmation is enabled
  • Confirmation email is not sent for duplicate signup attempts
  • User cannot complete registration despite entering valid credentials
  • Admin Auth returns status 422 error for duplicate emails

Common causes

  1. User attempting to create an account with an email that already exists in auth.users table
  2. Previous signup was completed but user forgot their credentials
  3. Application lacks proper pre-signup email existence check before calling signUp()
  4. Using supabase.auth.signUp() without verifying email uniqueness first
  5. User registered with OAuth provider using same email, then attempting password signup

How to fix "email_exists: Email address already exists"

1Check your email confirmation settings

Navigate to your Supabase Dashboard → Authentication → Settings and check the "Confirm email" option.

If disabled: Supabase will return a clear "email_exists" error that you can catch and handle directly.

If enabled: You'll receive an obfuscated/fake user response instead of an error, and need to implement the workarounds below.

2Detect existing emails by checking the identities array

When email confirmation is enabled, check the user.identities array in the signup response:

typescript
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'password123'
});

if (error?.message === 'email_exists') {
  // Email confirmation is disabled - direct error
  console.log('Email already registered - redirect to login');
  return;
}

if (data?.user) {
  // Email confirmation enabled - check identities array
  if (data.user.identities && data.user.identities.length === 0) {
    console.log('Email already exists - redirect to login');
    return;
  }

  // New user created successfully
  console.log('Signup successful - check email for verification');
}

An empty identities array indicates the email already exists.

3Use Admin Auth for server-side email checks

If you need definitive error messages, use the Admin Auth API with your service role key on the server side:

typescript
// Server-side only - never expose service_role key to client
import { createClient } from '@supabase/supabase-js';

const supabaseAdmin = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!,
  {
    auth: {
      autoRefreshToken: false,
      persistSession: false
    }
  }
);

const { data, error } = await supabaseAdmin.auth.admin.createUser({
  email: 'user@example.com',
  password: 'password123',
  email_confirm: true
});

if (error?.message?.includes('email already exists')) {
  // Email is registered - handle accordingly
  console.log('User already has account with this email');
  return;
}

// User created successfully
console.log('New user created');

Admin Auth returns clear errors even when email confirmation is enabled.

4Create a custom RPC function to check email existence

Create a PostgreSQL function in your Supabase SQL Editor to check if an email exists:

sql
CREATE OR REPLACE FUNCTION public.check_email_exists(email_to_check TEXT)
RETURNS BOOLEAN
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
  RETURN EXISTS (
    SELECT 1 FROM auth.users WHERE email = email_to_check
  );
END;
$$;

-- Grant execute permission to authenticated users
GRANT EXECUTE ON FUNCTION public.check_email_exists(TEXT) TO authenticated;

Then call it from your client before signup:

typescript
const { data: emailExists, error } = await supabase.rpc('check_email_exists', {
  email_to_check: 'user@example.com'
});

if (emailExists) {
  console.log('Email already registered - show login option');
  return;
}

// Email is free - proceed with signup
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'password123'
});
5Implement proper error handling in your signup form

Update your signup UI to gracefully handle email existence errors:

typescript
async function handleSignup(email: string, password: string) {
  const { data, error } = await supabase.auth.signUp({
    email,
    password
  });

  // Handle explicit email_exists error
  if (error?.message === 'email_exists') {
    showMessage('This email is already registered. Please log in or reset your password.');
    redirectToLogin();
    return;
  }

  // Handle other errors
  if (error) {
    showError(error.message);
    return;
  }

  // Check for obfuscated response (email confirmation enabled)
  if (data?.user?.identities?.length === 0) {
    showMessage('An account with this email already exists. Please log in.');
    redirectToLogin();
    return;
  }

  // Successful signup
  showMessage('Account created! Check your email to verify.');
}

Always provide clear guidance to users to either log in or reset their password.

Advanced notes

Security Implications of Email Confirmation Settings

Supabase's email confirmation behavior is designed to balance usability with security. When enabled, it prevents account enumeration attacks by not revealing whether an email is registered. When disabled, it provides better UX with clear errors but may leak information to attackers.

OAuth Integration Considerations

If your app uses OAuth providers (Google, GitHub, etc.), users who sign up with OAuth using the same email cannot later create a password-protected account with that email. Supabase automatically links OAuth identities to the same email address. Attempting to sign up with password will return an empty identities array (when confirmation is enabled) or email_exists error (when disabled).

Rate Limiting Your Email Checks

If you implement custom RPC functions or server-side checks to detect email existence, add rate limiting to prevent email enumeration attacks. An attacker could hammer your endpoint to discover registered emails.

Migration Path for Existing Users

When switching email confirmation settings, existing unconfirmed signups may behave unexpectedly. Consider reviewing your auth.users table and cleaning up stale unconfirmed accounts before changing settings.

Frequently asked questions

What does "email_exists: Email address already exists" mean?
users table. Supabase enforces email uniqueness to maintain data integrity and prevent duplicate accounts. The error's behavior depends on your project's email confirmation settings.
How do I fix "email_exists: Email address already exists"?
This error occurs when attempting to sign up a user with an email address already registered in Supabase Auth. The behavior depends on your email confirmation settings.
What causes "email_exists: Email address already exists"?
User attempting to create an account with an email that already exists in auth.users table Previous signup was completed but user forgot their credentials Application lacks proper pre-signup email existence check before calling signUp()
Is the "email_exists: Email address already exists" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading