Nextjs ResponseCookies function crashes with unhandled exception on decodeURIComponent if cookies has any string with % on it.
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.5k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://github.com/Sathosk/reponse-cookies-issue-reproduction-app
To Reproduce
- Run npm run dev
- Open localhost:3000
Current vs. Expected behavior
The application should handle the situation gracefully and not crash.
Instead, the following error occurs:
⨯ URIError: URI malformed
at decodeURIComponent (<anonymous>)
at Home (./src/app/page.tsx:11:78)
at AsyncLocalStorage.run (node:async_hooks:346:14)
at stringify (<anonymous>)
at AsyncResource.runInAsyncScope (node:async_hooks:206:9)
digest: "2977456002"
Provide environment information
Operating System:
Platform: win32
Arch: x64
Version: Windows 10 Pro
Available memory (MB): 16333
Available CPU cores: 12
Binaries:
Node: 20.14.0
npm: N/A
Yarn: N/A
pnpm: N/A
Relevant Packages:
next: 15.0.0-canary.171 // Latest available version is detected (15.0.0-canary.171).
eslint-config-next: N/A
react: 19.0.0-rc-778e1ed2-20240926
react-dom: 19.0.0-rc-778e1ed2-20240926
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Middleware, Runtime
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local), Other (Deployed)
Additional context
The issue seems to stem from the ResponseCookies function that Next.js provides for creating a new Set-Cookie header.
Before version 14.2.8, cookies set in middleware could not be synced with RSC due to the request-response cycle. To bypass this issue, I implemented a custom function:
function applySetCookie(req: NextRequest, res: NextResponse): void {
// parse the outgoing Set-Cookie header
const setCookieHeader = res.headers.getSetCookie()
const parsedCookies = parseSetCookies(setCookieHeader) // This used to be ResponseCookies function provided by Nextjs
// Build a new Cookie header for the request by adding the setCookies
const newReqHeaders = new Headers(req.headers)
const newReqCookies = new RequestCookies(newReqHeaders)
parsedCookies.forEach((cookie) => {
newReqCookies.set(cookie)
})
// set “request header overrides” on the outgoing response
NextResponse.next({
request: { headers: newReqHeaders },
}).headers.forEach((value, key) => {
if (
key === 'x-middleware-override-headers' ||
key.startsWith('x-middleware-request-')
) {
res.headers.set(key, value)
}
})
}
This approach worked, but I faced the same issue whenever a cookie contained a % character. It's not uncommon for cookies to have such characters.
The core issue here is that ResponseCookies is not handling exceptions thrown by the decodeURIComponent function. My workaround was to write a custom parser for handling cookies, and I have not faced any problems since.
However, starting with version 14.2.8, the functionality of merging cookies from middleware was added in the source code, essentially doing what I was doing. But the problem persists with the use of ResponseCookies, which crashes the application when decodeURIComponent throws an exception.
While I can implement a fix on my end, I believe this issue should be handled by the framework to prevent similar crashes.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Run the linked reproduction with npm run dev and inspect src/app/page.tsx at the reported decodeURIComponent call. Trace the ResponseCookies path used by middleware cookie merging, then add coverage for a cookie containing an invalid percent sequence. Done means the application handles that cookie without an unhandled URIError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- next.js, typescript
- Domain
- backend, web-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 57/100