This error occurs when attempting to link an OAuth identity to an existing user via linkIdentity() while the manual linking feature is disabled in your Supabase project settings. Manual linking must be explicitly enabled for security reasons.
This error indicates that you're trying to use Supabase's `linkIdentity()` method to manually link an OAuth provider identity (like Google, GitHub, or Apple) to an existing authenticated user, but the manual identity linking feature is not enabled in your project configuration. Manual identity linking is a security-sensitive feature that allows developers to programmatically connect multiple OAuth identities to a single user account. For example, a user who initially signed up with email/password could later link their Google or GitHub account to the same profile. Because this feature can be misused if not properly secured, Supabase disables it by default and requires explicit opt-in through project settings. When you call `linkIdentity()` without enabling this feature, Supabase's GoTrue authentication service returns a 404 error with the message "Manual linking is disabled" to prevent unauthorized identity manipulation.
For hosted Supabase projects, enable manual linking through the dashboard:
1. Navigate to your project in the Supabase Dashboard
2. Go to Authentication → Settings
3. Scroll to the Security and Protection section
4. Find the Enable Manual Linking option
5. Toggle it ON
6. Save your changes
The feature should be immediately available after enabling.
If you're self-hosting Supabase, add the environment variable to your GoTrue configuration:
GOTRUE_SECURITY_MANUAL_LINKING_ENABLED=trueAdd this to your .env file or Docker Compose configuration:
services:
auth:
environment:
GOTRUE_SECURITY_MANUAL_LINKING_ENABLED: trueRestart the GoTrue service after making this change.
If you're running Supabase locally and the environment variable doesn't work, your GoTrue version may be outdated:
# Update Supabase CLI
npm install -g supabase@latest
# Stop local services
supabase stop
# Pull latest service images
supabase db reset
# Start with updated services
supabase startVerify GoTrue is updated by checking the logs:
supabase statusEnsure the user is signed in before attempting to link an identity:
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
throw new Error('User must be authenticated to link identities');
}
// Now safe to call linkIdentity
const { data, error } = await supabase.auth.linkIdentity({
provider: 'google',
});
if (error) {
console.error('Failed to link identity:', error.message);
}Handle various error scenarios when linking identities:
async function linkGoogleAccount() {
try {
const { data, error } = await supabase.auth.linkIdentity({
provider: 'google',
});
if (error) {
if (error.message.includes('manual_linking_disabled')) {
console.error('Manual linking is not enabled in project settings');
// Show user-friendly message
} else if (error.message.includes('already linked')) {
console.error('This identity is already linked to another account');
} else {
console.error('Failed to link identity:', error);
}
return;
}
// For web: user will be redirected to OAuth provider
// For mobile: handle the response
console.log('Identity linking initiated:', data);
} catch (err) {
console.error('Unexpected error during identity linking:', err);
}
}Understanding Identity Linking Modes
Supabase offers two identity linking approaches:
1. Automatic Linking: Enabled by default. Supabase automatically links identities with matching email addresses to the same user account. This improves UX when users sign in with different OAuth providers.
2. Manual Linking: Requires explicit configuration. Gives developers programmatic control over linking identities via linkIdentity(), useful for cases like:
- Linking anonymous users to permanent accounts
- Adding OAuth providers to existing email/password accounts
- Building custom account management flows
Mobile Native OAuth Flows
For mobile apps using native OAuth (Google Sign-In SDK, Sign in with Apple), you can link identities using ID tokens:
const { data, error } = await supabase.auth.linkIdentity({
provider: 'google',
options: {
idToken: googleIdToken, // From native SDK
},
});Security Considerations
Manual linking is disabled by default because:
- Malicious code could link unauthorized identities to user accounts
- Improperly validated OAuth flows could enable account takeover
- Identity linking should be protected by proper user intent verification
Always verify user intent before linking identities and implement rate limiting for linking operations.
Unlinking Identities
Users must have at least 2 linked identities to unlink one:
// Get all identities
const { data: identities } = await supabase.auth.getUserIdentities();
if (identities && identities.length > 1) {
// Safe to unlink
await supabase.auth.unlinkIdentity({
identity_id: identityToRemove.id,
});
}Checking Configuration Status
There's no direct API to check if manual linking is enabled. The best approach is to catch the error and guide users to enable it in settings.
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