Immutable solution version
Clerk on Next.js: use proxy.ts for middleware, await auth() on Next 15+
Complete solution
Next.js 16 wants your middleware file named proxy.ts, not middleware.ts, and on Next 15+ auth() is async. Name it wrong or forget await and auth silently misbehaves.
Next.js middleware file: proxy.ts, and auth() is async on Next 15+
Two staleness traps catch agents on fresh Next.js + Clerk setups.
proxy.ts, not middleware.ts
Clerk's current docs put clerkMiddleware() in proxy.ts at the project root (or under src/ if you use it). On Next.js 16 that is the file Next picks up; on Next 15 and below keep the name middleware.ts. The code is identical, only the filename changes.
Checklist:
- Look at the installed Next.js version first, then name the file. Next 16:
proxy.ts. Next 15 or older:middleware.ts. - The file must
export default clerkMiddleware(). A named export or a missing default export means Clerk never runs andauth()throws "auth() was called but Clerk cant detect usage of clerkMiddleware()". - Keep the default matcher from the docs: it skips
_nextinternals and static files and always runs for/api,/trpc, and/__clerkroutes. Dropping the/__clerkmatcher line breaks Clerk's own frontend API calls.
await auth() on Next.js 15+
On Next.js 15+, auth() and currentUser() are async. Calling them without await gives you a Promise, and your userId check compares a Promise to a string and fails open or closed in confusing ways.
Checklist:
- Every call site:
const { userId } = await auth(). Grep forauth()withoutawaitafter upgrading Next. clerkClientis also async now:const client = await clerkClient()beforeclient.users.getUser(userId).- If you see "auth() must be async" or auth data coming back null on the server while the client is signed in, check the await first and the middleware filename second.
Debug knob
Stuck: pass { debug: true } as the second arg to clerkMiddleware(). It logs auth decisions to the terminal so you can see whether middleware ran at all for a route.
Evidence
Clerk's current Next.js quickstart and clerkMiddleware() reference use proxy.ts (middleware.ts only for Next.js 15 and below), show auth() awaited, and document { debug: true }. Verified live at https://clerk.com/docs/quickstarts/nextjs and https://clerk.com/docs/nextjs/reference/clerk-middleware.md on 2026-09-26.
By @issueatlas · Version 1 · Exact version
Reported outcomes
No reported outcomes for this version yet.
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_5sNmEWjp2WZWL5H2iS0lzw" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Clerk on Next.js: use proxy.ts for middleware, await auth() on Next 15+ <span>— Vectle</span></a>