VersionsEach version has its own postconditions. APIs and error modes shift across majors.
Profiles·Public

@clerk/nextjs

semver>=5.0.0 <7.0.0postconditions28functions20last verified2026-06-23coverage score100%

Postconditions: what we check

  • auth · missing-clerk-middleware
    error
    Whenauth() called without clerkMiddleware() configured in src/middleware.ts
    ThrowsError: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware()
    Required handlingMUST have clerkMiddleware() exported from src/middleware.ts with proper matcher configuration. Middleware must run for all routes where auth() is called.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • auth · auth-null-not-checked
    error
    Whenauth() result used without checking if userId exists
    ThrowsRuntime error when accessing properties of null/undefined user
    Required handlingMUST check auth().userId or auth().isAuthenticated before using authentication data. Pattern: `const { userId } = await auth(); if (!userId) return unauthorized();`
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • currentUser · current-user-not-cached
    warning
    WhencurrentUser() called multiple times in same request (Next.js 15+)
    ThrowsRate limit exceeded: HTTP 422 (100 req/10s per IP)
    Required handlingSHOULD wrap currentUser() with React cache() to avoid multiple API calls per request. Pattern: `const getCachedUser = cache(async () => await currentUser());`
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • currentUser · current-user-null-not-handled
    error
    WhencurrentUser() result used without null check
    ThrowsRuntime error accessing properties of null
    Required handlingMUST handle null case when currentUser() is called. Returns null if user not authenticated.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[4]
  • clerkMiddleware · middleware-not-exported
    error
    WhenclerkMiddleware defined but not exported from middleware.ts
    ThrowsMiddleware never runs, auth() calls fail
    Required handlingMUST export clerkMiddleware() as default export from src/middleware.ts. Pattern: `export default clerkMiddleware();`
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • clerkMiddleware · middleware-matcher-missing
    warning
    WhenclerkMiddleware without matcher configuration
    ThrowsMiddleware runs on static assets, causes 404 auth errors
    Required handlingSHOULD configure matcher to exclude static files and API routes that don't need auth. Pattern: `export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/'] };`
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • protect · protect-not-in-try-catch
    warning
    Whenauth.protect() called without try-catch in route handler
    ThrowsUncaught error causes 500 instead of proper 401/404
    Required handlingSHOULD wrap auth.protect() in try-catch to handle authentication errors gracefully. Returns 404 for unauthenticated requests with session token, 401 for machine tokens.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • create · signin-create-no-error-handling
    error
    WhensignIn.create() called without try-catch
    ThrowsClerkAPIError with error codes (user_locked, form_param_missing, etc.)
    Required handlingMUST wrap signIn.create() in try-catch and handle ClerkAPIError. Check error.errors[0].code for: user_locked, password_required, form_param_missing. For user_locked, display lockout_expires_in_seconds from error.meta.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • create · user-lockout-meta-not-displayed
    warning
    Whenuser_locked error caught but lockout_expires_in_seconds not shown to user
    ThrowsPoor UX - user doesn't know when to retry
    Required handlingSHOULD extract and display lockout_expires_in_seconds from error.meta when code is user_locked. Helps users know when account will unlock.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[7]
  • create · signup-create-no-error-handling
    error
    WhensignUp.create() called without try-catch
    ThrowsClerkAPIError with error codes (form_password_length_too_short, already_signed_in, etc.)
    Required handlingMUST wrap signUp.create() in try-catch and handle ClerkAPIError. Common errors: form_password_length_too_short, form_param_missing, requires_captcha.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • verify · webhook-signature-not-verified
    error
    WhenClerk webhook endpoint processes payload without signature verification
    ThrowsSecurity vulnerability - accepts forged webhook events
    Required handlingMUST verify webhook signature using Webhook.verify() from svix library. Use CLERK_WEBHOOK_SECRET environment variable. Pattern: `const wh = new Webhook(secret); const event = wh.verify(payload, headers);`
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • verify · webhook-verify-not-in-try-catch
    warning
    WhenWebhook.verify() called without try-catch
    ThrowsUnhandled error on invalid signature causes 500 instead of 400
    Required handlingSHOULD wrap Webhook.verify() in try-catch to return proper 400 response on verification failure.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • getToken · get-token-null-not-handled
    error
    WhengetToken() result used without null check
    ThrowsRuntime error when user not authenticated (returns null)
    Required handlingMUST handle null case when getToken() is called. Returns null if user not authenticated or token invalid.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[9]
  • setActive · set-active-no-error-handling
    warning
    WhensetActive() called without try-catch
    ThrowsError when session ID is invalid or session already active
    Required handlingSHOULD wrap setActive() in try-catch to handle invalid session errors. Can fail if session is null, invalid, or race conditions occur.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • useSignIn · use-signin-no-error-state
    warning
    WhenuseSignIn() used but errors not displayed to user
    ThrowsSilent failures - users don't know why sign-in failed
    Required handlingSHOULD maintain error state and display ClerkAPIError messages to users. Use isClerkAPIResponseError() to type-check errors.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • useClerk · use-clerk-outside-provider
    warning
    WhenuseClerk() called outside ClerkProvider
    ThrowsError: useClerk must be used within ClerkProvider
    Required handlingMUST ensure component using useClerk() is wrapped in ClerkProvider. In Next.js App Router, ClerkProvider should wrap root layout. NOTE: This is a likely false positive in Next.js App Router apps where ClerkProvider is in the root layout — static analysis cannot trace the component hierarchy. Verify manually that ClerkProvider is absent before treating as a real violation.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • verifyWebhook · verify-webhook-no-error-handling
    error
    WhenverifyWebhook() called without try-catch
    ThrowsThrows Error if CLERK_WEBHOOK_SIGNING_SECRET is not set, if svix headers are missing, or if the webhook signature is invalid. All three failure modes produce thrown errors.
    Required handlingMUST wrap verifyWebhook() in try-catch. On failure, return HTTP 400. Pattern: try { const evt = await verifyWebhook(req); } catch(err) { return new Response('Error', { status: 400 }); }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[11]
  • verifyWebhook · verify-webhook-missing-env-var
    error
    WhenverifyWebhook() used but CLERK_WEBHOOK_SIGNING_SECRET env var not configured
    ThrowsThrows Error: "Missing webhook signing secret. Set the CLERK_WEBHOOK_SIGNING_SECRET environment variable with the webhook secret from the Clerk Dashboard." Note: the variable is CLERK_WEBHOOK_SIGNING_SECRET, NOT CLERK_WEBHOOK_SECRET (a common misconfiguration from older Clerk docs).
    Required handlingMUST set CLERK_WEBHOOK_SIGNING_SECRET in environment variables. Get the signing secret from Clerk Dashboard → Webhooks → Endpoint → Signing Secret. The value starts with "whsec_". Note: different from CLERK_SECRET_KEY.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[11]
  • createUser · create-user-no-error-handling
    error
    When(await clerkClient()).users.createUser() called without try-catch
    ThrowsThrows ClerkAPIResponseError when: - email already exists (HTTP 422, code: form_identifier_exists) - rate limit exceeded (HTTP 429, 100/10s dev, 1000/10s prod) - required fields are missing (HTTP 400) - authentication fails (HTTP 401/403) Access error details via: error.errors[0].code, error.errors[0].message
    Required handlingMUST wrap in try-catch and handle ClerkAPIResponseError. Use isClerkAPIResponseError(error) from '@clerk/nextjs/errors' to type-guard. For duplicate email: check error.errors[0].code === 'form_identifier_exists'. For rate limits (429): implement retry with exponential backoff.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • deleteUser · delete-user-no-error-handling
    error
    When(await clerkClient()).users.deleteUser() called without try-catch
    ThrowsThrows ClerkAPIResponseError when: - user not found (HTTP 404) - userId is null/undefined (requireId() throws synchronously before HTTP call) - request fails (HTTP 400/401/429)
    Required handlingMUST wrap in try-catch. Deletion is irreversible — unhandled errors may leave partial state (e.g., database records exist but Clerk user deleted, or vice versa). Always delete from Clerk first, then from your database. If database deletion fails, you must manually re-create or alert — there is no rollback for Clerk deletion.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[13]
  • banUser · ban-user-no-error-handling
    error
    When(await clerkClient()).users.banUser() called without try-catch
    ThrowsThrows ClerkAPIResponseError when: - user not found (HTTP 404) - user is already banned (HTTP 422) - auth fails (HTTP 401/403)
    Required handlingMUST wrap in try-catch. A failed ban in an abuse-response flow means the malicious user remains active and can continue unauthorized actions. Log and alert on any ban failure. Do not silently swallow errors. Use isClerkAPIResponseError(error) to distinguish API failures from network timeouts.
    costhighin prodimmediate exceptionusers seesecurity breachvisibilityvisible
    Sources[14]
  • attemptFirstFactor · attempt-first-factor-no-error-handling
    error
    WhensignIn.attemptFirstFactor() called without try-catch
    ThrowsThrows ClerkAPIResponseError with codes: - form_password_incorrect: wrong password entered (HTTP 422) - user_locked: account locked after max failed attempts (HTTP 403), error.errors[0].meta.lockout_expires_in_seconds contains seconds until unlock - strategy_for_user_invalid: this auth strategy is not enabled for the user - verification_failed: code is wrong or expired (for OTP strategies)
    Required handlingMUST wrap in try-catch. Show specific error messages to users. For user_locked: display lockout_expires_in_seconds from error.errors[0].meta. For form_password_incorrect: show "Invalid credentials" (avoid confirming username existence). Use isClerkAPIResponseError(error) to type-check before accessing error.errors.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[15][16]
  • useUser · use-user-no-loaded-check
    warning
    WhenuseUser() result accessed before checking isLoaded
    ThrowsTypeError: Cannot read properties of undefined when accessing user.firstName, user.emailAddresses, etc. before isLoaded is true. user is undefined (not null) during Clerk's initial hydration phase.
    Required handlingMUST check isLoaded before accessing user properties. Pattern: const { user, isLoaded } = useUser(); if (!isLoaded) return <Skeleton />; Alternative: use <SignedIn> and <SignedOut> guard components which handle loading.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[17][18]
  • clerkFrontendApiProxy · proxy-missing-publishable-key
    error
    WhenclerkFrontendApiProxy() called without publishableKey in options or CLERK_PUBLISHABLE_KEY env var
    ThrowsReturns HTTP 500 Response with JSON body: { errors: [{ code: "proxy_configuration_error", message: "Missing publishableKey..." }] } Does NOT throw — returns a 500 error Response that is silently proxied to the client.
    Required handlingMUST ensure CLERK_PUBLISHABLE_KEY environment variable is set, or pass publishableKey in the options parameter. Without this, ALL Clerk frontend requests through the proxy will fail with 500 errors. Users will see a broken authentication UI with no clear error message since the 500 is returned as a JSON response, not a thrown exception.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[19]
  • clerkFrontendApiProxy · proxy-missing-secret-key
    error
    WhenclerkFrontendApiProxy() called without secretKey in options or CLERK_SECRET_KEY env var
    ThrowsReturns HTTP 500 Response with JSON body: { errors: [{ code: "proxy_configuration_error", message: "Missing secretKey..." }] } The Clerk-Secret-Key header is required for all proxied requests.
    Required handlingMUST ensure CLERK_SECRET_KEY environment variable is set. This is the same secret key used for other Clerk server-side operations. Without it, the proxy returns 500 for every request. Note: this key is server-side only and must NOT be prefixed with NEXT_PUBLIC_ — it is sent as the Clerk-Secret-Key header to Clerk's FAPI.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[19]
  • clerkFrontendApiProxy · proxy-network-failure-returns-502
    warning
    WhenclerkFrontendApiProxy() encounters a network error when forwarding to Clerk FAPI
    ThrowsReturns HTTP 502 Response with JSON body: { errors: [{ code: "proxy_request_failed", message: "Failed to proxy request to Clerk FAPI: <error>" }] } Network errors include DNS resolution failure, connection timeout, TLS handshake failure, or Clerk FAPI being down.
    Required handlingSHOULD monitor proxy route responses for 502 status codes. The proxy silently converts network errors to 502 responses — the calling code will not throw. If your proxy route is the only path for Clerk authentication, a 502 means ALL user auth is broken. Consider adding health check monitoring for the proxy endpoint.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[19]
  • verifyToken · verify-token-no-error-handling
    error
    WhenverifyToken() called without try-catch or .catch() handler
    ThrowsThrows TokenVerificationError when: - reason="token-expired": JWT exp claim is in the past (HTTP 401 expected) - reason="token-invalid": malformed JWT or wrong header type (HTTP 400) - reason="token-invalid-signature": signature does not match JWKS public key (HTTP 401) - reason="token-invalid-algorithm": JWT alg header is not RS256 or other accepted algorithm - reason="token-invalid-authorized-parties": azp claim missing from authorizedParties list - reason="token-not-active-yet": JWT nbf claim is in the future (clock skew) - reason="token-iat-in-the-future": JWT iat claim is in the future (clock skew) - reason="token-verification-failed": general verification failure (e.g. aud mismatch) - reason="secret-key-invalid": CLERK_SECRET_KEY env var missing or malformed - reason="jwk-remote-failed-to-load": Clerk JWKS endpoint unreachable (network failure) - reason="jwk-remote-missing" / "jwk-kid-mismatch": JWKS missing key for token's kid header Access: error.reason (one of TokenVerificationErrorReason values), error.action (one of TokenVerificationErrorAction values), error.message.
    Required handlingMUST wrap verifyToken() in try-catch. The dominant case (expired or missing tokens) is a normal unauthenticated request and should return HTTP 401, not 500. Pattern: try { const payload = await verifyToken(token, { secretKey: env.CLERK_SECRET_KEY }); // use payload.sub for userId } catch (err) { if (err instanceof TokenVerificationError) { return new Response('Unauthorized', { status: 401 }); } throw err; // re-throw unexpected errors } For JWKS network failures (reason: jwk-remote-failed-to-load), implement a retry with exponential backoff — a transient Clerk outage should not 500 the route.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[20][21]
  • useReverification · use-reverification-cancel-not-handled
    warning
    WhenuseReverification() wrapped fetcher called without handling isReverificationCancelledError
    ThrowsThe fetcher returned by useReverification() rejects with ClerkRuntimeError where err.code === "reverification_cancelled" when the user closes the reverification modal without completing it. Use isReverificationCancelledError(err) from '@clerk/nextjs/errors' to type-check. Distinct from network or API errors that may also reject from the underlying fetcher.
    Required handlingSHOULD catch and distinguish reverification cancellation from real errors. Pattern: const [enhancedFetcher] = useReverification(myFetcher); try { const result = await enhancedFetcher(); // success path } catch (err) { if (isReverificationCancelledError(err)) { // user cancelled — show retry button, not error toast return; } // genuine error — show error UI showError(err); } Without this distinction, a user who accidentally closes the modal sees a confusing error toast when nothing actually broke. The action also did not execute, so optimistic UI must be reverted.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[22][23]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Official documentation
Source code
Issues & pull requests

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

@clerk/nextjs Nark profile - Sources

Package: @clerk/nextjs (Clerk authentication SDK for Next.js) Version Range: >=5.0.0 <7.0.0 Last Verified: 2026-02-24 Research Thread: dev-notes/contexts/0010-clerk-nextjs-research.md


Official Documentation

  1. Clerk Error Handling Guide https://clerk.com/docs/guides/development/custom-flows/error-handling Comprehensive guide to handling authentication errors in custom flows

  2. Frontend API Error Codes https://clerk.com/docs/guides/development/errors/frontend-api Complete list of 200+ error codes with descriptions and error objects

  3. clerkMiddleware Reference https://clerk.com/docs/reference/nextjs/clerk-middleware Middleware configuration and usage patterns for Next.js

  4. auth() API Reference https://clerk.com/docs/reference/nextjs/app-router/auth Server-side authentication state retrieval in App Router

  5. currentUser() Reference https://clerk.com/docs/reference/nextjs/current-user Backend user data fetching with API implications

  6. Session Management & Token Refresh https://clerk.com/docs/guides/sessions/force-token-refresh Token lifecycle, automatic refresh (60s), and force refresh patterns

  7. Webhooks Overview https://clerk.com/docs/guides/development/webhooks/overview Webhook signature verification and event handling

  8. getToken() Reference https://clerk.com/docs/reference/nextjs/get-token Token retrieval with custom claims and skipCache option

  9. useClerk() Hook Reference https://clerk.com/docs/reference/clerk-react/use-clerk Client-side Clerk context access and setActive() usage


GitHub Issues (Real-World Error Patterns)

  1. Issue #4894: Rate Limiting with Next.js 15 https://github.com/clerk/javascript/issues/4894 Critical: Multiple currentUser() calls cause HTTP 422 rate limit errors Root cause: Next.js 15 changed fetch() caching defaults Impact: Shared IP addresses (Vercel) hit 100 req/10s limit Solution: Wrap currentUser() with React cache()

  2. Issue #4989: Session Encryption Errors https://github.com/clerk/javascript/issues/4989 Token decryption failures with error "Session encryption is not configured" Affects custom JWT claims and token validation

  3. Issue #1418: Infinite Redirect Loop https://github.com/clerk/javascript/issues/1418 Middleware configuration errors cause redirect loops Occurs when auth() called but clerkMiddleware() not properly set up

  4. Issue #1616: Token Refresh Failures https://github.com/clerk/javascript/issues/1616 Automatic token refresh (60s interval) fails under load Causes mid-session authentication failures

  5. Issue #XXXX: Asset 404 Loop Pattern documented in error handling guide Non-existent assets trigger 404 page without middleware auth() fails because clerkMiddleware() never runs on 404 route


Security Advisories

  1. GHSA-9mp4-77wg-rwx9: Webhook Signature Verification https://github.com/clerk/javascript/security/advisories/GHSA-9mp4-77wg-rwx9 Security advisory emphasizing webhook signature verification requirement Unverified webhooks allow attackers to forge user events

Real-World Usage Analysis

  1. Precedent (SaaS Boilerplate) File: test-repos/precedent/ Patterns observed: Middleware setup, auth() usage in route handlers Found: Proper error handling in sign-in forms

  2. Next-js-Boilerplate File: test-repos/Next-js-Boilerplate/ Patterns observed: ClerkProvider setup, useUser() hook usage Found: currentUser() called multiple times without caching

  3. Cal.com (Scheduling Platform) File: test-repos/cal.com/ Patterns observed: Webhook integration, auth.protect() usage Found: Webhook signature verification implemented

  4. Dub (Link Management) File: test-repos/dub/ Patterns observed: Middleware matcher configuration Found: Good middleware setup with proper matchers

  5. Documenso (Document Signing) File: test-repos/documenso/ Patterns observed: Session management, token refresh Found: Manual token refresh on API failures

  6. Taxonomy (Next.js Starter) File: test-repos/taxonomy/ Patterns observed: Basic auth() integration Found: Missing error handling in some API routes


Key Behavioral Insights

Critical Finding 1: The Middleware Dependency

@clerk/nextjs has a hard dependency on clerkMiddleware() for auth() to work:

// ❌ WRONG - auth() without middleware
// In app/api/users/route.ts
import { auth } from '@clerk/nextjs/server';

export async function GET() {
  const { userId } = await auth(); // ERROR: auth() called but clerkMiddleware not detected
}

// ✅ REQUIRED - middleware.ts must exist
// In src/middleware.ts
import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/'],
};

Source: https://clerk.com/docs/reference/nextjs/clerk-middleware

Critical Finding 2: Rate Limit Storm (Next.js 15)

Next.js 15 removed automatic fetch() caching, causing multiple API calls:

// ❌ WRONG - Hits rate limit (100 req/10s per IP)
export default async function Layout() {
  const user = await currentUser(); // Call #1
  return (
    <Navbar user={await currentUser()} /> // Call #2
    <Sidebar user={await currentUser()} /> // Call #3
  );
}

// ✅ CORRECT - Cache wrapper prevents multiple calls
import { cache } from 'react';
import { currentUser } from '@clerk/nextjs/server';

const getCachedUser = cache(async () => await currentUser());

export default async function Layout() {
  const user = await getCachedUser(); // Only 1 API call
  return (
    <Navbar user={user} />
    <Sidebar user={user} />
  );
}

Source: https://github.com/clerk/javascript/issues/4894

Critical Finding 3: Webhook Security Requirement

Clerk webhooks MUST be verified to prevent forged events:

// ❌ CRITICAL SECURITY FLAW - No signature verification
export async function POST(req: Request) {
  const event = await req.json();
  // Process event directly - ALLOWS FORGERY
  await db.updateUser(event.data.id);
}

// ✅ SECURE - Signature verification required
import { Webhook } from 'svix';

export async function POST(req: Request) {
  const payload = await req.text();
  const headers = {
    'svix-id': req.headers.get('svix-id'),
    'svix-timestamp': req.headers.get('svix-timestamp'),
    'svix-signature': req.headers.get('svix-signature'),
  };

  const wh = new Webhook(process.env.CLERK_WEBHOOK_SECRET!);

  try {
    const event = wh.verify(payload, headers);
    await db.updateUser(event.data.id); // Safe to process
  } catch (err) {
    return Response.json({ error: 'Invalid signature' }, { status: 400 });
  }
}

Source: https://github.com/clerk/javascript/security/advisories/GHSA-9mp4-77wg-rwx9

High-Priority Finding: Account Lockout UX

User lockout errors include retry timing metadata:

// ❌ POOR UX - User doesn't know when to retry
catch (err) {
  if (isClerkAPIResponseError(err)) {
    if (err.errors[0]?.code === 'user_locked') {
      setError('Account locked. Try again later.');
    }
  }
}

// ✅ GOOD UX - Show specific unlock time
catch (err) {
  if (isClerkAPIResponseError(err)) {
    const error = err.errors[0];
    if (error?.code === 'user_locked') {
      const expiresIn = error.meta?.lockout_expires_in_seconds || 900;
      const unlockTime = new Date(Date.now() + expiresIn * 1000);
      setError(`Account locked until ${unlockTime.toLocaleTimeString()}`);
    }
  }
}

Source: https://clerk.com/docs/guides/development/errors/frontend-api#user_locked


Implementation Status

Contract Version: 1.0.0 Last Verified: 2026-02-24 Analyzer Support: Partial (6/19 postconditions working)

Working Postconditions (6)

Null Check Detection (Phase 7.1):

  • auth-null-not-checked - Detects missing null checks after auth()
  • current-user-null-not-handled - Detects missing null checks after currentUser()
  • get-token-null-not-handled - Detects missing null checks after getToken()

File System Inspection (Phase 7.2):

  • middleware-not-exported - Validates middleware.ts exists and exports clerkMiddleware
  • middleware-matcher-missing - Checks for config.matcher export
  • missing-clerk-middleware - Detects auth() usage without middleware setup

Validation Results:

  • Tested against: precedent, Next-js-Boilerplate
  • True Positive Rate: 100%
  • False Positive Rate: 0%

Deferred Postconditions (13)

Require Complex Analysis:

  • webhook-signature-not-verified - Requires webhook pattern detection
  • webhook-verify-not-in-try-catch - Requires webhook pattern detection
  • current-user-not-cached - Requires multi-call tracking across file
  • use-clerk-outside-provider - Requires React component tree analysis
  • user-lockout-meta-not-displayed - Requires semantic error message analysis

Likely Working (Need Testing):

  • signin-create-no-error-handling - Generic try-catch detection
  • signup-create-no-error-handling - Generic try-catch detection
  • protect-not-in-try-catch - Generic try-catch detection
  • set-active-no-error-handling - Generic try-catch detection
  • use-signin-no-error-state - Client-side error state detection

Future Enhancements Needed:

  • Multi-call detection within same file/component
  • Webhook signature verification pattern matching
  • React component tree analysis for provider wrapping
  • Error message content analysis

Analyzer Enhancements Delivered

Phase 7.1: Null Check Detection

  • Handles destructuring: const { userId } = await auth()
  • Handles direct assignment: const user = await currentUser()
  • Detects compound conditions: if (!isAuthenticated || !userId)
  • Supports optional chaining: user?.emailAddresses
  • Recursive AST traversal for || and && operators

Phase 7.2: File System Inspection

  • checkFileExists() - Search root/, src/, app/ directories
  • checkFileImportsAndExports() - AST-based import/export validation
  • checkClerkMiddlewareExists() - Middleware configuration check
  • Broadly applicable to 40-60% of future packages

Error Categories

Authentication & Authorization (P0 - Critical)

  • authentication_invalid - Invalid credentials
  • authorization_invalid - Insufficient permissions
  • session_reverification_required - Session needs re-auth
  • Missing middleware error - auth() without clerkMiddleware()

Session Management (P0 - Critical)

  • token_expired - JWT expired (60s default)
  • token_refresh_failed - Auto-refresh mechanism failed
  • Rate limit exceeded - HTTP 422 (100 req/10s)
  • Token verification failures with custom aud claims

Sign In/Up Flows (P1 - High)

  • user_locked - Account lockout after max attempts (HTTP 403)
    • Includes lockout_expires_in_seconds in meta
  • user_banned - Permanently blocked
  • password_required - Missing password
  • form_param_missing - Required field missing
  • form_password_length_too_short - Validation failed

OAuth & SSO (P1 - High)

  • oauth_config_missing - Provider not configured
  • oauth_account_already_connected - Duplicate OAuth connection
  • oauth_provider_not_enabled - Provider disabled

Webhooks (P0 - Critical)

  • Signature mismatch - Tampered payload
  • Payload verification failures
  • Missing CLERK_WEBHOOK_SECRET

Rate Limiting (P0 - Critical)

  • HTTP 429 - Rate limit exceeded
  • action_blocked - Fraud detection
  • requires_captcha - CAPTCHA verification needed

Testing Strategy

Fixtures validate:

  1. Middleware dependency (auth() without clerkMiddleware)
  2. Null handling (auth(), currentUser(), getToken())
  3. Error handling (signIn.create(), signUp.create())
  4. Webhook signature verification
  5. Rate limiting (multiple currentUser() calls)
  6. Error state display (useSignIn, useSignUp)

Real-world validation against:

  • precedent (6 repos total)
  • Next-js-Boilerplate
  • cal.com
  • dub
  • documenso
  • taxonomy

Related Packages

  • @clerk/clerk-react - React-only Clerk package
  • @auth0/nextjs-auth0 - Alternative auth provider (separate contract)
  • next-auth - Open-source auth library (separate contract)
  • svix - Webhook signature verification (used by Clerk)

Research completed: 2026-02-24 Total sources: 21 references GitHub issues analyzed: 5 Real-world repos: 6 Confidence level: HIGH

Need a different package?
Request a profile