Utilities for detecting and handling different types of Clerk errors. These error detection functions help you handle specific error scenarios in your application.
Required Setup:
@clerk/nextjs/errorsDefault Behaviors:
boolean (true if error matches type)ClerkAPIResponseError has status, errors array, and clerkError properties'form_identifier_not_found')EmailLinkErrorCode constants available for email link error codesThreading Model:
Lifecycle:
Edge Cases:
isKnownError() to check if error is Clerk-relatederr.errors[0]?.code for ClerkAPIResponseErrorerr.errors[0]?.message for user-friendly messagesmeta property with additional contextExceptions:
false (not Clerk error)falseFunctions to identify specific types of Clerk errors.
/**
* Checks if error is a Clerk API response error
* API errors occur when Clerk backend returns an error response
* @param err - Error to check
* @returns True if error is ClerkAPIResponseError
*/
function isClerkAPIResponseError(err: unknown): boolean;
/**
* Checks if error is a Clerk runtime error
* Runtime errors occur during Clerk SDK operations
* @param err - Error to check
* @returns True if error is ClerkRuntimeError
*/
function isClerkRuntimeError(err: unknown): boolean;
/**
* Checks if error is an email link error
* Email link errors occur during email verification flows
* @param err - Error to check
* @returns True if error is EmailLinkError
*/
function isEmailLinkError(err: unknown): boolean;
/**
* Checks if error is a known Clerk error
* Known errors include all Clerk-specific error types
* @param err - Error to check
* @returns True if error is a known Clerk error
*/
function isKnownError(err: unknown): boolean;
/**
* Checks if error is a reverification cancelled error
* Occurs when user cancels reverification flow
* @param err - Error to check
* @returns True if error is reverification cancelled
*/
function isReverificationCancelledError(err: unknown): boolean;
/**
* Checks if error is a Metamask-related error
* Occurs during Metamask authentication flows
* @param err - Error to check
* @returns True if error is MetamaskError
*/
function isMetamaskError(err: unknown): boolean;Usage Example - API Route Error Handling:
import {
isClerkAPIResponseError,
isClerkRuntimeError,
isKnownError,
} from '@clerk/nextjs/errors';
import { clerkClient } from '@clerk/nextjs/server';
export async function POST(req: Request) {
try {
const { userId } = await req.json();
const client = await clerkClient();
const user = await client.users.getUser(userId);
return Response.json({ user });
} catch (err) {
// Check if it's a Clerk API error
if (isClerkAPIResponseError(err)) {
console.error('Clerk API error:', err);
return Response.json(
{
error: 'Failed to fetch user from Clerk',
details: err.errors,
},
{ status: err.status }
);
}
// Check if it's a Clerk runtime error
if (isClerkRuntimeError(err)) {
console.error('Clerk runtime error:', err);
return Response.json(
{ error: 'Clerk SDK error' },
{ status: 500 }
);
}
// Check if it's any known Clerk error
if (isKnownError(err)) {
console.error('Known Clerk error:', err);
return Response.json(
{ error: 'Clerk operation failed' },
{ status: 400 }
);
}
// Unknown error
console.error('Unknown error:', err);
return Response.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}Usage Example - Client Component Error Handling:
'use client';
import { useSignIn } from '@clerk/nextjs';
import {
isClerkAPIResponseError,
isKnownError,
} from '@clerk/nextjs/errors';
import { useState } from 'react';
export default function SignInForm() {
const { signIn, setActive } = useSignIn();
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError(null);
const formData = new FormData(e.currentTarget);
const email = formData.get('email') as string;
const password = formData.get('password') as string;
try {
const result = await signIn?.create({
identifier: email,
password,
});
if (result?.status === 'complete') {
await setActive({ session: result.createdSessionId });
}
} catch (err) {
// Handle Clerk API errors
if (isClerkAPIResponseError(err)) {
if (err.errors[0]?.code === 'form_identifier_not_found') {
setError('Email not found');
} else if (err.errors[0]?.code === 'form_password_incorrect') {
setError('Incorrect password');
} else {
setError('Sign in failed. Please try again.');
}
return;
}
// Handle other known errors
if (isKnownError(err)) {
setError('An error occurred. Please try again.');
return;
}
// Unknown error
console.error('Unknown error:', err);
setError('An unexpected error occurred');
}
};
return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Sign In</button>
</form>
);
}When using isClerkAPIResponseError, the error has specific properties:
/**
* Clerk API Response Error structure
*/
interface ClerkAPIResponseError extends Error {
/**
* HTTP status code
*/
status: number;
/**
* Error code from Clerk
*/
clerkError: boolean;
/**
* Array of error details
*/
errors: ClerkAPIError[];
}
interface ClerkAPIError {
/**
* Error code (e.g., 'form_identifier_not_found')
*/
code: string;
/**
* Human-readable error message
*/
message: string;
/**
* Long-form error message
*/
longMessage?: string;
/**
* Field that caused the error (if applicable)
*/
meta?: {
paramName?: string;
};
}Usage Example - Detailed Error Handling:
import { isClerkAPIResponseError } from '@clerk/nextjs/errors';
import { clerkClient } from '@clerk/nextjs/server';
export async function POST(req: Request) {
try {
const { email } = await req.json();
const client = await clerkClient();
const user = await client.users.createUser({
emailAddress: [email],
});
return Response.json({ user });
} catch (err) {
if (isClerkAPIResponseError(err)) {
// Log all error details
console.error('Clerk API Error:', {
status: err.status,
errors: err.errors.map(e => ({
code: e.code,
message: e.message,
field: e.meta?.paramName,
})),
});
// Handle specific error codes
const errorCode = err.errors[0]?.code;
if (errorCode === 'form_param_format_invalid') {
return Response.json(
{ error: 'Invalid email format' },
{ status: 400 }
);
}
if (errorCode === 'form_identifier_exists') {
return Response.json(
{ error: 'Email already exists' },
{ status: 400 }
);
}
// Generic API error response
return Response.json(
{
error: err.errors[0]?.message || 'API request failed',
},
{ status: err.status }
);
}
// Unknown error
return Response.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}Email link errors occur during email verification flows.
/**
* Email link error codes
*/
const EmailLinkErrorCode: {
/**
* Email link has expired
*/
Expired: 'expired';
/**
* Email link has already been used
*/
Failed: 'failed';
/**
* Email link is not found
*/
NotFound: 'not_found';
/**
* Email verification is not supported
*/
NotSupported: 'not_supported';
/**
* Client mismatch error
*/
ClientMismatch: 'client_mismatch';
};
/**
* Email link error code status mapping
*/
const EmailLinkErrorCodeStatus: {
expired: number;
failed: number;
not_found: number;
not_supported: number;
client_mismatch: number;
};Usage Example - Email Link Error Handling:
'use client';
import { useSignUp } from '@clerk/nextjs';
import {
isEmailLinkError,
EmailLinkErrorCode,
} from '@clerk/nextjs/errors';
import { useEffect, useState } from 'react';
export default function EmailVerificationPage() {
const { signUp } = useSignUp();
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const verifyEmailLink = async () => {
try {
// Verify email link from URL
await signUp?.attemptEmailAddressVerification({
code: 'email_link',
});
} catch (err) {
if (isEmailLinkError(err)) {
// Handle specific email link errors
if (err.code === EmailLinkErrorCode.Expired) {
setError('Email verification link has expired. Please request a new one.');
} else if (err.code === EmailLinkErrorCode.Failed) {
setError('Email verification failed. Please try again.');
} else if (err.code === EmailLinkErrorCode.NotFound) {
setError('Email verification link not found.');
} else if (err.code === EmailLinkErrorCode.ClientMismatch) {
setError('Please open the link in the same browser where you started sign-up.');
} else {
setError('Email verification error. Please try again.');
}
} else {
setError('An unexpected error occurred.');
}
}
};
verifyEmailLink();
}, [signUp]);
if (error) {
return <div className="error">{error}</div>;
}
return <div>Verifying email...</div>;
}Handle reverification cancellation errors.
/**
* Reverification cancelled error
* Occurs when user cancels reverification flow
*/
interface ReverificationCancelledError extends Error {
code: 'reverification_cancelled';
}Usage Example - Reverification Error:
'use client';
import { useUser } from '@clerk/nextjs';
import { isReverificationCancelledError } from '@clerk/nextjs/errors';
export default function SensitiveAction() {
const { user } = useUser();
const handleSensitiveAction = async () => {
try {
// Perform action that requires reverification
await user?.update({
password: 'new-password',
});
} catch (err) {
if (isReverificationCancelledError(err)) {
console.log('User cancelled reverification');
// Don't show error, just cancel the action
return;
}
// Handle other errors
console.error('Action failed:', err);
alert('Failed to update password');
}
};
return (
<button onClick={handleSensitiveAction}>
Change Password
</button>
);
}Handle Metamask-specific authentication errors.
/**
* Metamask error
* Occurs during Metamask authentication flows
*/
interface MetamaskError extends Error {
code: string;
metamaskError: boolean;
}Usage Example - Metamask Error:
'use client';
import { useClerk } from '@clerk/nextjs';
import { isMetamaskError } from '@clerk/nextjs/errors';
export default function MetamaskSignIn() {
const { authenticateWithMetamask } = useClerk();
const handleMetamaskSignIn = async () => {
try {
await authenticateWithMetamask();
} catch (err) {
if (isMetamaskError(err)) {
if (err.code === 'metamask_not_installed') {
alert('Please install Metamask to continue');
} else if (err.code === 'metamask_user_rejected') {
console.log('User rejected Metamask connection');
} else {
alert('Metamask authentication failed');
}
return;
}
// Handle other errors
console.error('Authentication error:', err);
alert('Failed to authenticate with Metamask');
}
};
return (
<button onClick={handleMetamaskSignIn}>
Sign in with Metamask
</button>
);
}Clerk API errors include specific error codes for different scenarios:
/**
* Common Clerk error codes
*/
type ClerkErrorCode =
// Form validation errors
| 'form_param_format_invalid'
| 'form_param_missing'
| 'form_param_nil'
| 'form_param_value_invalid'
| 'form_param_unknown'
| 'form_param_max_length_exceeded'
| 'form_param_min_length_not_met'
// Authentication errors
| 'form_identifier_not_found'
| 'form_password_incorrect'
| 'form_identifier_exists'
| 'session_exists'
| 'not_allowed_to_sign_up'
// Resource errors
| 'resource_not_found'
| 'resource_invalid'
// Authorization errors
| 'authorization_invalid'
| 'client_state_invalid'
// Rate limiting
| 'rate_limit_exceeded'
// Two-factor authentication
| 'verification_required'
| 'verification_expired'
| 'verification_failed'
// Organization errors
| 'organization_domain_common'
| 'organization_invitation_already_accepted'
// Billing errors
| 'billing_invalid_subscription'
| 'billing_quota_exceeded';Usage Example - Error Code Mapping:
import { isClerkAPIResponseError } from '@clerk/nextjs/errors';
function getErrorMessage(err: unknown): string {
if (!isClerkAPIResponseError(err)) {
return 'An unexpected error occurred';
}
const code = err.errors[0]?.code;
const errorMessages: Record<string, string> = {
form_identifier_not_found: 'Email or username not found',
form_password_incorrect: 'Incorrect password',
form_identifier_exists: 'This email is already registered',
form_param_format_invalid: 'Invalid format',
rate_limit_exceeded: 'Too many attempts. Please try again later.',
verification_expired: 'Verification code expired. Please request a new one.',
verification_failed: 'Invalid verification code',
not_allowed_to_sign_up: 'Sign up is not allowed',
resource_not_found: 'Resource not found',
authorization_invalid: 'Unauthorized',
};
return errorMessages[code] || err.errors[0]?.message || 'Request failed';
}
// Usage in error handler
export async function POST(req: Request) {
try {
// ... Clerk operation
} catch (err) {
const message = getErrorMessage(err);
return Response.json({ error: message }, { status: 400 });
}
}Handle errors gracefully without breaking user experience:
'use client';
import { useUser } from '@clerk/nextjs';
import { isClerkAPIResponseError } from '@clerk/nextjs/errors';
import { useState } from 'react';
export default function UserProfile() {
const { user } = useUser();
const [error, setError] = useState<string | null>(null);
const [isUpdating, setIsUpdating] = useState(false);
const updateProfile = async (data: any) => {
setIsUpdating(true);
setError(null);
try {
await user?.update(data);
alert('Profile updated successfully');
} catch (err) {
if (isClerkAPIResponseError(err)) {
setError(err.errors[0]?.message || 'Update failed');
} else {
setError('An unexpected error occurred');
}
} finally {
setIsUpdating(false);
}
};
return (
<div>
{error && <div className="error">{error}</div>}
<button
onClick={() => updateProfile({ firstName: 'John' })}
disabled={isUpdating}
>
Update Profile
</button>
</div>
);
}Convert technical errors to user-friendly messages:
import { isClerkAPIResponseError } from '@clerk/nextjs/errors';
function getUserFriendlyError(err: unknown): string {
if (!isClerkAPIResponseError(err)) {
return 'Something went wrong. Please try again.';
}
const code = err.errors[0]?.code;
// Map technical codes to friendly messages
switch (code) {
case 'form_identifier_not_found':
return "We couldn't find an account with that email.";
case 'form_password_incorrect':
return 'The password you entered is incorrect.';
case 'form_identifier_exists':
return 'An account with this email already exists.';
case 'rate_limit_exceeded':
return "You've made too many attempts. Please wait a few minutes and try again.";
default:
return err.errors[0]?.message || 'Something went wrong. Please try again.';
}
}Log errors for debugging and monitoring:
import { isClerkAPIResponseError, isKnownError } from '@clerk/nextjs/errors';
function logError(err: unknown, context: string) {
if (isClerkAPIResponseError(err)) {
console.error(`[${context}] Clerk API Error:`, {
status: err.status,
errors: err.errors,
timestamp: new Date().toISOString(),
});
} else if (isKnownError(err)) {
console.error(`[${context}] Known Clerk Error:`, {
error: err,
timestamp: new Date().toISOString(),
});
} else {
console.error(`[${context}] Unknown Error:`, {
error: err,
timestamp: new Date().toISOString(),
});
}
// Send to error tracking service
// trackError(err, context);
}
// Usage
export async function POST(req: Request) {
try {
// ... operation
} catch (err) {
logError(err, 'POST /api/users');
return Response.json({ error: 'Request failed' }, { status: 500 });
}
}Implement retry logic for transient errors:
import { isClerkAPIResponseError } from '@clerk/nextjs/errors';
async function retryOperation<T>(
operation: () => Promise<T>,
maxRetries = 3
): Promise<T> {
let lastError: unknown;
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (err) {
lastError = err;
// Don't retry on client errors (4xx)
if (isClerkAPIResponseError(err) && err.status < 500) {
throw err;
}
// Wait before retrying (exponential backoff)
if (i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
throw lastError;
}
// Usage
export async function POST(req: Request) {
try {
const result = await retryOperation(async () => {
const client = await clerkClient();
return await client.users.getUser('user_123');
});
return Response.json({ result });
} catch (err) {
return Response.json({ error: 'Operation failed' }, { status: 500 });
}
}All error utilities are exported from @clerk/nextjs/errors:
import {
isClerkAPIResponseError,
isClerkRuntimeError,
isEmailLinkError,
isKnownError,
isReverificationCancelledError,
isMetamaskError,
EmailLinkErrorCode,
EmailLinkErrorCodeStatus,
} from '@clerk/nextjs/errors';