How to fix identity_not_found: Identity does not exist in Supabase

SupabaseINTERMEDIATEMEDIUMUpdated Dec 12, 2025ยท 174 views

Supabase returns 'identity_not_found: Identity does not exist' when attempting to link, unlink, or reference an identity that has been deleted or doesn't exist in the auth.identities table. This typically occurs during OAuth linking operations, user merging, or when identities are manually removed from the database. The fix involves verifying the identity exists, recreating it if needed, and ensuring proper linking procedures.

Quick fix

Fixes identity_not_found

import { createClient } from '@supabase/supabase-js'

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
)

// Check for a specific identity by provider and provider ID
const { data: identities, error } = await supabaseAdmin
  .from('auth.identities')
  .select('*')
  .eq('provider', 'github')
  .eq('provider_id', '123456789') // The provider's user ID
  .single()

if (error) {
  console.log('Identity not found or query error:', error.message)
} else {
  console.log('Identity exists:', identities)
}
  1. Verify the identity exists in auth.identities
  2. Recreate the missing identity if needed
  3. Check for identity conflicts and user associations
  4. Implement proper error handling and user messaging
supabase โ€” error
import { createClient } from '@supabase/supabase-js'
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
)
// Check for a specific identity by provider and provider ID
const { data: identities, error } = await supabaseAdmin
.from('auth.identities')
.select('*')
.eq('provider', 'github')
.eq('provider_id', '123456789') // The provider's user ID
.single()
if (error) {
console.log('Identity not found or query error:', error.message)
} else {
console.log('Identity exists:', identities)
}
identity_not_found: Identity does not exist

What this error means

The Supabase Auth system maintains a separate 'auth.identities' table that stores authentication identities (OAuth providers, email/password, phone, etc.) linked to user accounts. When you attempt to perform operations like linking a new OAuth provider to an existing user, unlinking an identity, or referencing an identity for authentication, Supabase checks this table first. The error "identity_not_found: Identity does not exist" means: - The identity you're trying to reference (by provider, ID, or user association) doesn't exist in the 'auth.identities' table - The identity may have been previously unlinked or deleted - You might be trying to link an identity that's already associated with another user - The identity record could be corrupted or missing due to manual database operations This is different from "user_not_found" because the user exists, but the specific identity (authentication method) you're trying to use doesn't.

Typical symptoms

  • Attempts to link OAuth providers fail with HTTP 400 and the message "identity_not_found: Identity does not exist".
  • Unlinking identities (like removing GitHub from a user account) returns the same error.
  • User authentication works with some providers but fails with others that were previously linked.
  • Database queries to auth.identities show missing rows for expected provider-user combinations.
  • User migration or account merging operations fail due to missing identity records.

Common causes

  1. The identity was manually deleted from the auth.identities table via SQL or the Management API.
  2. An attempt to link an OAuth provider that was previously unlinked without proper cleanup.
  3. Database corruption or migration issues that removed identity records.
  4. Trying to reference an identity that belongs to a different user (identity_already_exists scenario that manifests as not found).
  5. Race conditions during identity linking/unlinking operations.
  6. Using deprecated or incorrect identity IDs that don't match the stored records.

How to fix "identity_not_found: Identity does not exist"

1Verify the identity exists in auth.identities

Use the service-role key to inspect the auth.identities table and confirm the identity record:

javascript
import { createClient } from '@supabase/supabase-js'

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
)

// Check for a specific identity by provider and provider ID
const { data: identities, error } = await supabaseAdmin
  .from('auth.identities')
  .select('*')
  .eq('provider', 'github')
  .eq('provider_id', '123456789') // The provider's user ID
  .single()

if (error) {
  console.log('Identity not found or query error:', error.message)
} else {
  console.log('Identity exists:', identities)
}

You can also query directly via SQL:

sql
SELECT id, user_id, provider, provider_id, identity_data, created_at, updated_at
FROM auth.identities
WHERE provider = 'github' AND provider_id = '123456789';

This confirms whether the identity row exists and which user it's associated with.

2Recreate the missing identity if needed

If the identity is genuinely missing but should exist, you may need to recreate it. For OAuth identities, this typically requires the user to re-authenticate:

javascript
// For OAuth providers, trigger a new OAuth flow
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: {
    redirectTo: 'https://yourapp.com/auth/callback'
  }
})

if (error) {
  console.error('OAuth error:', error.message)
} else {
  // User will be redirected to GitHub for authentication
  // Upon return, the identity will be recreated
}

For email/password identities that are missing, you might need to:

javascript
// Use the Management API to create or update the user
const { data, error } = await supabaseAdmin.auth.admin.updateUserById(
  userId,
  {
    email: 'user@example.com',
    password: 'new-password-if-needed'
  }
)

Note: Recreating identities should be done carefully to avoid creating duplicate accounts.

3Check for identity conflicts and user associations

Verify that the identity isn't already associated with a different user (which would cause identity_already_exists in some cases):

javascript
// Check all identities for a specific provider ID
const { data: allIdentities, error } = await supabaseAdmin
  .from('auth.identities')
  .select('user_id, provider, provider_id')
  .eq('provider_id', '123456789') // The provider's user ID

if (allIdentities && allIdentities.length > 0) {
  console.log('This identity is associated with user(s):', allIdentities)

  // If it's associated with a different user than expected
  const expectedUserId = 'expected-user-uuid'
  const actualUserIds = allIdentities.map(id => id.user_id)

  if (!actualUserIds.includes(expectedUserId)) {
    console.log('Identity belongs to different user(s): ' + actualUserIds.join(', '))
    console.log('Expected it to belong to: ' + expectedUserId)
  }
}

If the identity belongs to a different user, you may need to:
1. Unlink it from the wrong user (if you have permission)
2. Link it to the correct user
3. Or ask the user to use a different OAuth account

4Implement proper error handling and user messaging

When this error occurs in your application, provide clear guidance to users:

javascript
import { isAuthApiError } from '@supabase/supabase-js'

try {
  const { data, error } = await supabase.auth.linkIdentity({
    provider: 'github'
  })

  if (error) {
    if (isAuthApiError(error) && error.code === 'identity_not_found') {
      // User-friendly message
      setErrorMessage(
        'Unable to link your GitHub account. The account connection appears to be missing. ' +
        'Please try signing in with GitHub again to re-establish the connection.'
      )

      // Log for debugging
      console.error('Identity not found details:', {
        code: error.code,
        status: error.status,
        message: error.message
      })
    }
  }
} catch (error) {
  console.error('Unexpected error:', error)
}

For unlinking operations:

javascript
if (error?.code === 'identity_not_found') {
  // The identity is already gone, so operation is effectively successful
  console.log('Identity already removed, cleaning up UI state')
  removeProviderFromUI('github')
}
5Audit and prevent future identity loss

Implement monitoring and safeguards to prevent identity records from going missing:

javascript
// Regular audit of user-identity associations
async function auditUserIdentities(userId) {
  const { data: user } = await supabaseAdmin.auth.admin.getUserById(userId)
  const { data: identities } = await supabaseAdmin
    .from('auth.identities')
    .select('*')
    .eq('user_id', userId)

  console.log('User ' + userId + ' has ' + (identities?.length || 0) + ' identities')

  // Check for common issues
  if (user && identities && identities.length === 0) {
    console.warn('User ' + userId + ' has no identities - this may cause auth issues')
    // Implement recovery logic or alert
  }
}

// Add database constraints or triggers if you have direct access
// (Note: Requires database access beyond standard Supabase API)

// Implement backup of critical auth data
async function backupAuthData() {
  // Export auth.users and auth.identities regularly
  // Store backups securely for disaster recovery
}

Consider implementing:
1. Regular audits of identity-user associations
2. Alerts for users with zero identities (can't authenticate)
3. Backup strategies for auth data
4. Documentation of manual identity recovery procedures

Advanced notes

### How Supabase manages identities

Supabase stores identities separately from users in the 'auth.identities' table. Each identity row contains:
- 'id': Internal UUID for the identity record
- 'user_id': Reference to the 'auth.users' row
- 'provider': Authentication provider (email, phone, github, google, etc.)
- 'provider_id': The provider's unique identifier for this user
- 'identity_data': JSON blob with provider-specific data (profile info, tokens, etc.)

### Identity lifecycle

1. Creation: When a user signs up or links a new provider
2. Linking: Adding additional identities to an existing user
3. Unlinking: Removing an identity from a user (user must have at least one identity left)
4. Deletion: When a user is deleted, all their identities are cascade-deleted

### Common pitfalls

- Manual SQL operations: Direct database modifications can break the relationship between users and identities
- OAuth provider changes: If a user changes their OAuth account (e.g., GitHub username), the provider_id changes, creating what appears to be a "missing" identity
- Import/export issues: Migrating users between projects without their identities
- Race conditions: Concurrent linking/unlinking operations can leave inconsistent state

### Recovery strategies

For critical production issues:
1. Use database backups to restore missing identities
2. Implement phased rollout of identity-related features
3. Maintain audit logs of all identity operations
4. Consider implementing a reconciliation job that periodically fixes orphaned or missing identities

Frequently asked questions

What does "identity_not_found" mean?
) linked to user accounts. When you attempt to perform operations like linking a new OAuth provider to an existing user, unlinking an identity, or referencing an identity for authentication, Supabase checks this table first. identities' table - The identity may have been previously unlinked or deleted - You might be trying to link an identity that's already associated with another user - The identity record could be corrupted or missing due to manual database operations This is different from "user_not_found" because the user exists, but the specific identity (authentication method) you're trying to use doesn't.
How do I fix "identity_not_found"?
Supabase returns 'identity_not_found: Identity does not exist' when attempting to link, unlink, or reference an identity that has been deleted or doesn't exist in the auth.identities table. This typically occurs during OAuth linking operations, user merging, or when identities are manually removed from the database. The fix involves verifying the identity exists, recreating it if needed, and ensuring proper linking procedures.
What causes "identity_not_found"?
The identity was manually deleted from the auth.identities table via SQL or the Management API. An attempt to link an OAuth provider that was previously unlinked without proper cleanup. Database corruption or migration issues that removed identity records.
Is the "identity_not_found" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading