Preliminary Checks
Reproduction
https://github.com/gyulavoros/clerk-body-issue
Publishable key
pk_test_bmF0aXZlLXBob2VuaXgtODcuY2xlcmsuYWNjb3VudHMuZGV2JA
Description
ClerkRequest's constructor in packages/backend/src/tokens/clerkRequest.ts transfers the request body when it only needs headers and cookies.
When called as new ClerkRequest(request) (the common path from authenticateRequest):
const url = typeof input !== 'string' && 'url' in input ? input.url : String(input);
super(url, init || typeof input === 'string' ? undefined : input);
This resolves to super(urlString, request), which passes the entire Request as init. The Request constructor reads init.body and transfers the ReadableStream, leaving the original request's body locked/consumed.
When this matters:
In runtimes where the Request body is a native buffered object (Bun.serve, Cloudflare Workers), the body survives the transfer. But when the body is a standard ReadableStream — as is the case in Node.js-based dev servers like Vite — the transfer consumes it. Any middleware or handler downstream that reads the body (e.g. tRPC, form handlers) gets "Body already used".
Reproduction:
Create a Hono app with clerkMiddleware() applied globally
Add a POST route that reads req.json() or req.formData()
Run through any Node.js-based server (Vite dev server, Express adapter, etc.) where the Request is constructed from an IncomingMessage with a ReadableStream body
POST to that route → "Body already used"
Suggested fix:
ClerkRequest only uses headers and cookies — it never reads the body. The constructor should avoid transferring it:
super(url, init || typeof input === 'string' ? undefined : { method: input.method, headers: input.headers });
Environment
System:
OS: macOS 26.3.1
CPU: (10) arm64 Apple M1 Max
Memory: 331.73 MB / 32.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.7.0 - /Users/gyulavoros/.nvm/versions/node/v24.7.0/bin/node
npm: 11.5.1 - /Users/gyulavoros/.nvm/versions/node/v24.7.0/bin/npm
pnpm: 10.33.0 - /opt/homebrew/bin/pnpm
bun: 1.3.12 - /Users/gyulavoros/.bun/bin/bun
Browsers:
Chrome: 147.0.7727.56
Firefox: 133.0
Safari: 26.3.1
npmPackages:
@clerk/hono: 0.1.11 => 0.1.11
@vikejs/hono: 0.2.0 => 0.2.0
@vitejs/plugin-react: 5.1.4 => 5.1.4
hono: 4.12.4 => 4.12.4
react: 19.2.4 => 19.2.4
react-dom: 19.2.4 => 19.2.4
vike: 0.4.257 => 0.4.257
vike-react: 0.6.20 => 0.6.20
vite: 8.0.8 => 8.0.8
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 (if you have, no need to open an issue here)
This issue is not a question, general help request, or anything other than a bug report directly related to Clerk. Please ask questions in our Discord community: https://clerk.com/discord.
Reproduction
https://github.com/gyulavoros/clerk-body-issue
Publishable key
pk_test_bmF0aXZlLXBob2VuaXgtODcuY2xlcmsuYWNjb3VudHMuZGV2JA
Description
ClerkRequest's constructor inpackages/backend/src/tokens/clerkRequest.tstransfers the request body when it only needs headers and cookies.When called as
new ClerkRequest(request)(the common path fromauthenticateRequest):This resolves to
super(urlString, request), which passes the entireRequestasinit. TheRequestconstructor readsinit.bodyand transfers theReadableStream, leaving the original request's body locked/consumed.When this matters:
In runtimes where the
Requestbody is a native buffered object (Bun.serve, Cloudflare Workers), the body survives the transfer. But when the body is a standardReadableStream— as is the case in Node.js-based dev servers like Vite — the transfer consumes it. Any middleware or handler downstream that reads the body (e.g. tRPC, form handlers) gets"Body already used".Reproduction:
Create a Hono app with
clerkMiddleware()applied globallyAdd a POST route that reads
req.json()orreq.formData()Run through any Node.js-based server (Vite dev server, Express adapter, etc.) where the
Requestis constructed from anIncomingMessagewith aReadableStreambodyPOST to that route →
"Body already used"Suggested fix:
ClerkRequestonly uses headers and cookies — it never reads the body. The constructor should avoid transferring it:Environment
System: OS: macOS 26.3.1 CPU: (10) arm64 Apple M1 Max Memory: 331.73 MB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 24.7.0 - /Users/gyulavoros/.nvm/versions/node/v24.7.0/bin/node npm: 11.5.1 - /Users/gyulavoros/.nvm/versions/node/v24.7.0/bin/npm pnpm: 10.33.0 - /opt/homebrew/bin/pnpm bun: 1.3.12 - /Users/gyulavoros/.bun/bin/bun Browsers: Chrome: 147.0.7727.56 Firefox: 133.0 Safari: 26.3.1 npmPackages: @clerk/hono: 0.1.11 => 0.1.11 @vikejs/hono: 0.2.0 => 0.2.0 @vitejs/plugin-react: 5.1.4 => 5.1.4 hono: 4.12.4 => 4.12.4 react: 19.2.4 => 19.2.4 react-dom: 19.2.4 => 19.2.4 vike: 0.4.257 => 0.4.257 vike-react: 0.6.20 => 0.6.20 vite: 8.0.8 => 8.0.8