This error occurs when attempting to unlink or delete a user's only authentication identity in Supabase. Users must have at least one identity to maintain account access.
Supabase Auth enforces a security constraint that prevents users from removing their last authentication identity. An identity represents a way a user can sign in to your application, such as email/password, Google OAuth, GitHub OAuth, or other authentication providers. When you call the `unlinkIdentity()` method and the user only has one identity linked to their account, Supabase returns the "single_identity_not_deletable" error. This safeguard ensures users don't accidentally lock themselves out of their accounts by removing all authentication methods. This error most commonly occurs when implementing account settings pages that allow users to manage their linked authentication providers, or when attempting to migrate users from one authentication method to another without proper sequencing.
Before attempting to unlink an identity, check how many identities the user has linked:
const { data: { user } } = await supabase.auth.getUser();
if (user && user.identities) {
console.log(`User has ${user.identities.length} identities`);
console.log(user.identities);
}If the user only has one identity, you cannot unlink it.
To change a user's authentication method, first add the new identity, then remove the old one:
// First, link the new identity (e.g., Google OAuth)
const { data, error: linkError } = await supabase.auth.linkIdentity({
provider: 'google'
});
if (linkError) {
console.error('Error linking identity:', linkError);
return;
}
// Wait for the linking process to complete
// The user will be redirected to the OAuth provider
// After successful linking, you can now unlink the old identity
const { data: unlinkData, error: unlinkError } = await supabase.auth.unlinkIdentity({
identity_id: 'old-identity-id'
});Note: Enable "Manual Linking" in your Supabase project settings under Authentication > Settings.
When implementing identity management features, detect and handle this error:
async function handleUnlinkIdentity(identityId: string) {
const { data: { user } } = await supabase.auth.getUser();
// Check if user has multiple identities
if (user?.identities && user.identities.length <= 1) {
alert('Cannot remove your only login method. Please add another identity first.');
return;
}
const { error } = await supabase.auth.unlinkIdentity({
identity_id: identityId
});
if (error) {
if (error.message.includes('single_identity_not_deletable')) {
alert('You must have at least one identity linked to your account.');
} else {
console.error('Error unlinking identity:', error);
}
}
}To use the linkIdentity() and unlinkIdentity() methods:
1. Go to your Supabase Dashboard
2. Navigate to Authentication > Settings
3. Scroll to "Auth Providers" or "Manual Linking"
4. Enable "Manual Linking" option
5. Save changes
Without this setting enabled, identity linking operations will fail.
If you need to completely change a user's authentication method (for example, migrating from email/password to OAuth), consider using the Admin API to create a new user with the desired identity, migrate their data, and then delete the old user account. This approach avoids the single identity constraint.
For scenarios where you want to force-remove a user's identity for security reasons (e.g., compromised OAuth provider), you'll need to use the Admin API deleteUser() method instead, which removes the entire user account. There is no way to bypass the single identity constraint for active users.
The identity object contains an identity_id field (not the same as user_id) which uniquely identifies each authentication method. When working with multiple identities, ensure you're using the correct identity_id for the specific provider you want to unlink.
email_conflict_identity_not_deletable: Cannot delete identity because of email conflict
How to fix "Cannot delete identity because of email conflict" in Supabase
mfa_challenge_expired: MFA challenge has expired
How to fix "mfa_challenge_expired: MFA challenge has expired" in Supabase
conflict: Database conflict, usually related to concurrent requests
How to fix "database conflict usually related to concurrent requests" in Supabase
phone_exists: Phone number already exists
How to fix "phone_exists" in Supabase
StorageApiError: resource_already_exists
StorageApiError: Resource already exists