Skip to content

avoid double encoding path params - #87003

Open
therungg wants to merge 8 commits into
vercel:canaryfrom
therungg:fix/avoid-double-encoding-path-params
Open

avoid double encoding path params#87003
therungg wants to merge 8 commits into
vercel:canaryfrom
therungg:fix/avoid-double-encoding-path-params

Conversation

@therungg

@therungg therungg commented Dec 9, 2025

Copy link
Copy Markdown

What?

This encodes and re-decodes the path param before throwing an error from decodeURIComponent.

Why?

More often than not, the error is thrown when a string given to decodeURIComponent has already been decoded. This means that a string like %25 will already be decoded to %, which results in an error from decodeURIComponent. This is also the case for this function. The parameter given is already encoded once, so this function will fail with %25. This PR fixes that by applying the standard solution to this problem: re-encoding, then re-decoding.

If decodeURIComponent still throws an error, the same DecodeError as before is thrown, because that means something different is the culprit.

How?

By re-encoding, then re-decoding the string.

Closes NEXT-
Fixes #86957

@nextjs-bot

nextjs-bot commented Dec 9, 2025

Copy link
Copy Markdown
Contributor

Allow CI Workflow Run

  • approve CI run for commit: 812b914

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@therungg
therungg force-pushed the fix/avoid-double-encoding-path-params branch from 212d508 to 3a0cf26 Compare December 9, 2025 22:50
@harikapadia999

Copy link
Copy Markdown

Interesting approach to the double-encoding issue! 🤔

Problem: When a URL parameter is already decoded (like % from %25), calling decodeURIComponent() again throws an error.

Your Solution: Re-encode the param first, then decode it.

However, I have concerns:

  1. This creates actual double-encoding: If the param was hello%20world (already encoded), your code would:

    • Encode: hello%2520world
    • Decode: hello%20world
    • Result: Still encoded, not the expected hello world
  2. The test doesn't validate the fix: Your test uses '/' + alreadyDecodedUrlPart which is '/%', but the route matcher receives this as a string, not a URL-encoded value. The test should use '/%25' to simulate the actual scenario.

Better Solution:

try {
  return decodeURIComponent(param)
} catch {
  // Param is already decoded or invalid
  return param
}

This handles both cases:

  • Already decoded params: returns as-is
  • Encoded params: decodes properly
  • Invalid params: returns as-is (graceful degradation)

Test Improvement:

const result = routeMatcher('/%25') // Encoded %
expect(result).toEqual({ user: '%' }) // Decoded result

The intent is good, but the implementation needs adjustment to avoid creating new encoding issues. 🚀

@therungg

Copy link
Copy Markdown
Author

I think this is a fully AI-generated response, because pretty much all of the above is incorrect. It only encodes when the initial decoding fails, so there's never double encoding. The test is correct, because I want to test if the routeMatcher handles the already-encoded /% correctly.

@therungg

Copy link
Copy Markdown
Author

Hi,

Is anyone able to look at this? It's a crucial bug for my app. Looking forward to getting this merged. Do let me know if there's anything I can change.

Thanks!

@filmon-arefayne

filmon-arefayne commented Mar 9, 2026

Copy link
Copy Markdown

@timneutkens any chance that this will get merged?
This issue is preventing users from using cacheComponents in Nextjs 16.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Internal Server Error on dynamic routes with %25 in the dynamic param with cacheComponents enabled

4 participants