Clerk SDK for Next.js providing authentication and user management with support for both App Router and Pages Router architectures
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';Install with Tessl CLI
npx tessl i tessl/npm-clerk--nextjs