Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Collect comprehensive debug information for Clerk issues. Use when troubleshooting complex problems, preparing support tickets, or diagnosing intermittent issues. Trigger with phrases like "clerk debug", "clerk diagnostics", "clerk support ticket", "clerk troubleshooting".
.claude/skills/jeremylongshore-clerk-debug-bundle/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 135% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 23% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 69% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 78% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 55% | 0% |
!node --version 2>/dev/null || echo 'N/A' !npm list @clerk/nextjs @clerk/clerk-react @clerk/express 2>/dev/null | grep clerk || echo 'No Clerk packages found'
Collect all necessary debug information for Clerk troubleshooting and support tickets. Generates an environment report, runtime health check, client-side debug panel, and support bundle.
typescript// scripts/clerk-debug.ts import { createClerkClient } from '@clerk/backend' async function collectDebugInfo() { const info: Record<string, any> = { timestamp: new Date().toISOString(), nodeVersion: process.version, platform: process.platform, env: { hasPK: !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, hasSK: !!process.env.CLERK_SECRET_KEY, pkPrefix: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY?.slice(0, 8) + '...', nodeEnv: process.env.NODE_ENV, }, } // Test API connectivity try { const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! }) const users = await clerk.users.getUserList({ limit: 1 }) info.apiConnectivity = { status: 'ok', userCount: users.totalCount } } catch (err: any) { info.apiConnectivity = { status: 'error', message: err.message, code: err.status } } // Check package versions try { const pkg = require('./package.json') info.packages = Object.entries(pkg.dependencies || {}) .filter(([k]) => k.includes('clerk')) .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) } catch { info.packages = 'Could not read package.json' } console.log(JSON.stringify(info, null, 2)) return info } collectDebugInfo()
Run with:
bashnpx tsx scripts/clerk-debug.ts
typescript// app/api/clerk-health/route.ts import { auth, clerkClient } from '@clerk/nextjs/server' export async function GET() { const checks: Record<string, { status: string; detail?: string }> = {} // Check 1: SDK loaded checks.sdk = { status: 'ok', detail: 'Clerk SDK loaded' } // Check 2: Auth function works try { const { userId } = await auth() checks.auth = { status: 'ok', detail: userId ? `Authenticated as ${userId}` : 'Not authenticated (expected for health check)' } } catch (err: any) { checks.auth = { status: 'error', detail: err.message } } // Check 3: Backend API connectivity try { const client = await clerkClient() await client.users.getUserList({ limit: 1 }) checks.backendApi = { status: 'ok', detail: 'API reachable' } } catch (err: any) { checks.backendApi = { status: 'error', detail: err.message } } // Check 4: Environment variables checks.envVars = { status: process.env.CLERK_SECRET_KEY ? 'ok' : 'error', detail: process.env.CLERK_SECRET_KEY ? 'Secret key configured' : 'CLERK_SECRET_KEY missing', } const allOk = Object.values(checks).every((c) => c.status === 'ok') return Response.json({ healthy: allOk, checks }, { status: allOk ? 200 : 503 }) }
typescript'use client' import { useAuth, useUser, useSession } from '@clerk/nextjs' import { useState } from 'react' export function ClerkDebugPanel() { const { userId, isLoaded: authLoaded, getToken } = useAuth() const { user, isLoaded: userLoaded } = useUser() const { session } = useSession() const [tokenPreview, setTokenPreview] = useState<string | null>(null) if (process.env.NODE_ENV === 'production') return null // Hide in prod const inspectToken = async () => { const token = await getToken() if (token) { const payload = JSON.parse(atob(token.split('.')[1])) setTokenPreview(JSON.stringify(payload, null, 2)) } } return ( <details style={{ position: 'fixed', bottom: 10, right: 10, background: '#1a1a2e', color: '#eee', padding: 12, borderRadius: 8, fontSize: 12, zIndex: 9999 }}> <summary>Clerk Debug</summary> <pre> Auth loaded: {String(authLoaded)}{'\n'} User loaded: {String(userLoaded)}{'\n'} User ID: {userId || 'null'}{'\n'} Email: {user?.primaryEmailAddress?.emailAddress || 'N/A'}{'\n'} Session ID: {session?.id || 'null'}{'\n'} Session status: {session?.status || 'N/A'}{'\n'} Last active: {session?.lastActiveAt ? new Date(session.lastActiveAt).toISOString() : 'N/A'} </pre> <button onClick={inspectToken}>Inspect JWT</button> {tokenPreview && <pre>{tokenPreview}</pre>} </details> ) }
typescript// middleware.ts — add debug logging (development only) import { clerkMiddleware } from '@clerk/nextjs/server' export default clerkMiddleware(async (auth, req) => { if (process.env.CLERK_DEBUG === 'true') { const { userId, sessionId } = await auth() console.log(`[Clerk Debug] ${req.method} ${req.nextUrl.pathname}`, { userId: userId || 'anonymous', sessionId: sessionId?.slice(0, 8) || 'none', cookies: req.cookies.getAll().map((c) => c.name).filter((n) => n.startsWith('__clerk')), }) } })
bash#!/bin/bash # scripts/clerk-support-bundle.sh set -euo pipefail BUNDLE_DIR="clerk-debug-$(date +%Y%m%d-%H%M%S)" mkdir -p "$BUNDLE_DIR" # Package versions npm list --depth=0 2>/dev/null | grep clerk > "$BUNDLE_DIR/packages.txt" || true # Environment check (redacted) echo "NODE_ENV: ${NODE_ENV:-not set}" > "$BUNDLE_DIR/env.txt" echo "Has PK: $([ -n "${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:-}" ] && echo yes || echo no)" >> "$BUNDLE_DIR/env.txt" echo "Has SK: $([ -n "${CLERK_SECRET_KEY:-}" ] && echo yes || echo no)" >> "$BUNDLE_DIR/env.txt" echo "PK prefix: ${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:0:8}..." >> "$BUNDLE_DIR/env.txt" # Middleware check [ -f middleware.ts ] && cp middleware.ts "$BUNDLE_DIR/" || echo "No middleware.ts found" > "$BUNDLE_DIR/middleware-missing.txt" # Health check curl -s http://localhost:3000/api/clerk-health > "$BUNDLE_DIR/health.json" 2>/dev/null || echo '{"error":"app not running"}' > "$BUNDLE_DIR/health.json" # Bundle it tar czf "${BUNDLE_DIR}.tar.gz" "$BUNDLE_DIR" rm -rf "$BUNDLE_DIR" echo "Support bundle: ${BUNDLE_DIR}.tar.gz"
/api/clerk-health endpoint for runtime health checks| Issue | Debug Action | |-------|--------------| | Auth not working | Hit /api/clerk-health, check backendApi status | | Token issues | Use debug panel "Inspect JWT" to view claims and expiry | | Middleware not running | Enable CLERK_DEBUG=true, check console for request logs | | Session not persisting | Check debug panel for __clerk cookies, verify domain |
bash# Verify Clerk API connectivity from CLI curl -s -H "Authorization: Bearer $CLERK_SECRET_KEY" \ https://api.clerk.com/v1/users?limit=1 | jq '.total_count'
Proceed to clerk-rate-limits for understanding Clerk rate limits.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | fail→pass | 7,316 | 3,951 | -46% | 1 | 1 | 0% | 1,274 | 3,000 | +135% | 0 | 0 | — |
case-10 | pass→pass | 10,413 | 5,550 | -47% | 1 | 1 | 0% | 1,956 | 3,239 | +66% | 0 | 0 | — |
case-11 | pass→pass | 5,291 | 2,004 | -62% | 1 | 1 | 0% | 887 | 2,678 | +202% | 0 | 0 | — |
case-12 | pass→pass | 11,789 | 5,343 | -55% | 1 | 1 | 0% | 2,091 | 3,317 | +59% | 0 | 0 | — |
case-13 | pass→pass | 13,026 | 8,741 | -33% | 1 | 1 | 0% | 2,644 | 4,128 | +56% | 0 | 0 | — |
case-14 | pass→pass | 13,172 | 8,849 | -33% | 1 | 1 | 0% | 2,504 | 3,815 | +52% | 0 | 0 | — |
case-01 | fail→pass | 18,679 | 11,775 | -37% | 1 | 1 | 0% | 3,955 | 4,876 | +23% | 0 | 0 | — |
case-02 | fail→pass | 18,518 | 19,312 | +4% | 1 | 1 | 0% | 4,108 | 6,941 | +69% | 0 | 0 | — |
case-03 | fail→fail | 21,142 | 11,835 | -44% | 1 | 1 | 0% | 4,183 | 4,900 | +17% | 0 | 0 | — |
case-04 | pass→fail | 18,137 | 15,757 | -13% | 1 | 1 | 0% | 3,507 | 5,293 | +51% | 0 | 0 | — |
case-05 | fail→pass | 15,971 | 15,747 | -1% | 1 | 1 | 0% | 3,082 | 5,489 | +78% | 0 | 0 | — |
case-06 | pass→pass | 20,953 | 17,454 | -17% | 1 | 1 | 0% | 4,226 | 5,869 | +39% | 0 | 0 | — |
case-07 | fail→fail | 14,098 | 8,273 | -41% | 1 | 1 | 0% | 2,678 | 3,896 | +45% | 0 | 0 | — |
case-08 | pass→pass | 10,796 | 8,013 | -26% | 1 | 1 | 0% | 1,821 | 3,626 | +99% | 0 | 0 | — |
case-15 | pass→pass | 9,660 | 3,028 | -69% | 1 | 1 | 0% | 1,823 | 2,910 | +60% | 0 | 0 | — |
case-16 | fail→fail | 11,596 | 5,525 | -52% | 1 | 1 | 0% | 2,034 | 3,271 | +61% | 0 | 0 | — |
case-17 | pass→pass | 13,339 | 6,068 | -55% | 1 | 1 | 0% | 2,294 | 3,433 | +50% | 0 | 0 | — |
case-18 | fail→pass | 10,255 | 3,026 | -70% | 1 | 1 | 0% | 1,807 | 2,794 | +55% | 0 | 0 | — |
case-19 | fail→pass | 15,046 | 8,751 | -42% | 1 | 1 | 0% | 2,737 | 3,940 | +44% | 0 | 0 | — |
case-20 | fail→fail | 12,219 | 8,294 | -32% | 1 | 1 | 0% | 1,939 | 3,716 | +92% | 0 | 0 | — |
case-21 | fail→pass | 5,803 | 3,618 | -38% | 1 | 1 | 0% | 1,142 | 2,888 | +153% | 0 | 0 | — |
case-22 | fail→pass | 8,338 | 4,338 | -48% | 1 | 1 | 0% | 1,342 | 2,967 | +121% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.