Dev instances: clerk-js rewrites __clerk_db_jwt non-atomically, concurrent requests fail with dev-browser-missing
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
- #10412 de @umeraamir69 — fechado sem integrar
Avaliação
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Facilidade para iniciantes
- 35/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Estagnada
- Stack de tecnologia
- next.js, node.js, typescript
- Domínio
- authentication, backend
Direção de pesquisa
The report identifies the dev browser cookie handler in clerk-js 6.40.1 dist/clerk.js and AuthenticateContext.initHandshakeValues() in @clerk/backend as relevant entry points. Read the cookie rewrite and suffixed-cookie lookup, then reproduce the missing-suffix state with the browser-console steps and a middleware route. Done means concurrent requests no longer get dev-browser-missing during cookie rewrites; linked pull request #10412 is already open.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Preliminary Checks
- I have reviewed the documentation: https://clerk.com/docs
- I have searched for existing issues: https://github.com/clerk/javascript/issues
- I have not already reached out to Clerk support via email or Discord
- This issue is a bug report directly related to Clerk
Reproduction
No separate repository: the intermediate cookie state can be reproduced deterministically from the browser console in any Next.js app on a development instance (steps below). The race itself shows up in our Playwright suite on CI.
Publishable key
Development instance; available on request.
Description
On development instances, clerk-js rewrites the dev browser cookie non-atomically. A request to the app that leaves the browser during the rewrite carries __clerk_db_jwt but not __clerk_db_jwt_<suffix>, and clerkMiddleware treats it as signed out with reason dev-browser-missing, even though the session cookies are present and valid.
Where it comes from (clerk-js 6.40.1, dist/clerk.js, dev browser cookie handler):
o = () => { let e = iR(r); for (let t of [s, i]) t.remove(e), t.remove(), t.remove(iN) },
// ...
set: e => { /* ... */ o(), s.set(e, {...}), i.set(e, {...}) }
s is the suffixed cookie and i the unsuffixed one. Every set first removes both (three removals each), then sets the suffixed one, then the unsuffixed one. set runs on every FAPI response carrying a Clerk-Db-Jwt header (onAfterResponse), so it happens several times right after each page load (client, token, organization lists) and on every token refresh.
On the server, AuthenticateContext.initHandshakeValues() (@clerk/backend 3.23.2 and 3.26.2) reads the dev browser token with getSuffixedOrUnSuffixedCookie, which only looks at __clerk_db_jwt_<suffix> when usesSuffixedCookies() is true. With the suffixed cookie missing, authenticateRequestWithTokenInCookie returns DevBrowserMissing. For a document GET the handshake recovers, but:
- a
fetchto a route handler gets a 401; - a server action gets redirected to the sign-in URL and silently has no effect;
- an RSC prefetch caches the signed-out payload, so a later client navigation lands on the sign-in page.
Evidence: we instrumented signedOut() in @clerk/backend to log the cookie names it saw when it returned signed out. Every dev-browser-missing case had __session, __session_<suffix>, __client_uat, __client_uat_<suffix> and __clerk_db_jwt, but no __clerk_db_jwt_<suffix>, which is exactly the state between s.remove(...) and i.remove(...) above. We also saw (less often) requests with neither variant, which is the state between the last removal and s.set(...). The window widens under CPU load: it hit our E2E suite several times per run on 2-vCPU CI runners, and we reproduced it locally by running Playwright with 6 to 8 workers.
Steps to reproduce (deterministic, simulating the intermediate state):
- Run a Next.js app with
clerkMiddlewareon a development instance and sign in. - In the browser console, remove only the suffixed dev browser cookie and call any route that goes through the middleware:
const suffixed = document.cookie.split('; ').find((c) => c.startsWith('__clerk_db_jwt_')).split('=')[0];
document.cookie = `${suffixed}=; Max-Age=0; path=/`;
const r = await fetch('/any-route-behind-clerkMiddleware');
console.log(r.status, r.headers.get('x-clerk-auth-status'), r.headers.get('x-clerk-auth-reason'));
// -> signed-out, dev-browser-missing (401 for an API route)
- Reload: clerk-js writes the cookie again and everything works.
Expected behavior:
Rewriting the dev browser cookie should never leave a window where a signed-in browser sends an incomplete set. For example, set the new values first and only then remove stale variants with different attributes (or skip the removal when attributes don't change). On the backend, falling back to the unsuffixed __clerk_db_jwt when the suffixed one is missing would also close the most common window, since clerk-js always writes both with the same value.
Actual behavior:
Intermittent signed-out / dev-browser-missing for requests that carry a valid session, with silent failures in server actions and fetches.
Workaround we use: with a pk_test_ key, our middleware copies __clerk_db_jwt into __clerk_db_jwt_<suffix> (suffix taken from the __session_<suffix> cookie) when only the unsuffixed one is present, before calling clerkMiddleware.
Environment
@clerk/nextjs: 7.9.15
@clerk/backend: 3.23.2 (same lookup in 3.26.2)
@clerk/clerk-js: 6.40.1 (loaded from the instance CDN, latest 6.x)
@clerk/testing: 2.2.45
next: 16.4.1 (also seen with 16.4.0), proxy (middleware) on the Node.js runtime
Browser: Chromium via Playwright 1.64
OS: Ubuntu 24.04 (GitHub Actions, 2 vCPU) and macOS 27
Node.js: from .node-version (24.x)
- Linguagem predominante
- TypeScript
- Estrelas
- 1.8k
- Forks
- 483
- Merge médio
- 2d 1h
- PRs com merge (30d)
- 346
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de clerk/javascript
-
useLoadedIsomorphicClerk misses "ready" when a ClerkProvider remounts after clerk-js has loaded — <ClerkLoading> never resolvesTalvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
clerk/javascript#10462 ·
Mantenedores costumam responder em até 1 dia
-
ClerkAPIError drops meta.lockout_expires_in_seconds from user_locked errorsTalvez já em andamento @umeraamir69 assumiu há 6 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
clerk/javascript#10026 · 2 comentários ·
Mantenedores costumam responder em até 1 dia
-
@clerk/nextjs: onBeforeSetActive never settles when invalidateCacheAction rejects (e.g. after a redeploy), so setActive and signOut hang foreverTalvez já em andamento @RaphaelFakhri assumiu há 22 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
clerk/javascript#9987 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
needs-triage
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 44/100
clerk/javascript#10413 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
needs-triage
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 55/100
clerk/javascript#10356 ·
Mantenedores costumam responder em até 1 dia
Todas as issues de clerk/javascript
Issues semelhantes
-
bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
scaleway/ultraviolet#7089 ·
Mantenedores costumam responder em até 2 dias
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 67/100
tencent-connect/dsh-qqbot#81 ·
Mantenedores costumam responder em até 1 dia
-
[Bug]: Pen taps on sidebar rows start a drag and lose their clickTalvez já em andamento @makhnatkin assumiu hoje. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
Mantenedores costumam responder em até 1 dia
-
bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 66/100
clangd/vscode-clangd#1009 ·