Skip to content

Instantly share code, notes, and snippets.

@davidbarratt
Created August 4, 2026 19:38
Show Gist options
  • Select an option

  • Save davidbarratt/08d1334f2c9dde6fa5cde0da7c3a4030 to your computer and use it in GitHub Desktop.

Select an option

Save davidbarratt/08d1334f2c9dde6fa5cde0da7c3a4030 to your computer and use it in GitHub Desktop.
Minimal reproduction: URIError: URI malformed in Clerk's ClerkRequest.parseCookies

Minimal reproduction: URIError: URI malformed in ClerkRequest.parseCookies

ClerkRequest.decodeCookieValue runs decodeURIComponent over the raw Cookie header without a try/catch, so any malformed percent-escape in any cookie on the domain throws inside the ClerkRequest constructor.

Source: https://github.com/clerk/javascript/blob/438f2e5220e82dfefb1dfbe9e6ea4c989aac5194/packages/backend/src/tokens/clerkRequest.ts#L96-L103

Run

npm install
node repro.mjs

Output (@clerk/backend@3.15.1, Node 24.18.0)

URIError: URI malformed
    at decodeURIComponent (<anonymous>)
    at String.replace (<anonymous>)
    at ClerkRequest.decodeCookieValue (.../@clerk/backend/dist/chunk-DDFIPK3V.mjs:6491:22)
    at ClerkRequest.parseCookies (.../@clerk/backend/dist/chunk-DDFIPK3V.mjs:6487:57)
    at new ClerkRequest (.../@clerk/backend/dist/chunk-DDFIPK3V.mjs:6450:25)
    at createClerkRequest (.../@clerk/backend/dist/chunk-DDFIPK3V.mjs:6496:37)

No Clerk instance, publishable key, or network access is needed — the throw happens in a string function during construction, before any Clerk API call.

createClerkRequest is the entry point used by authenticateRequest, so clerkMiddleware() fails the same way for real requests carrying such a cookie.

{
"name": "clerk-decode-cookie-value-repro",
"private": true,
"type": "module",
"dependencies": {
"@clerk/backend": "3.15.1"
}
}
import { createClerkRequest } from '@clerk/backend/internal';
// `analytics_id` holds a value whose percent-escape is truncated — what happens
// when a client-side script writes a value that gets clipped at the browser's
// ~4096-byte per-cookie limit in the middle of an escape sequence like %E2%9C%93.
// The cookie is unrelated to Clerk; it just shares the domain.
const request = new Request('https://example.com/', {
headers: { cookie: '__session=abc; analytics_id=%E2%9' },
});
createClerkRequest(request); // URIError: URI malformed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment