Skip to content

fix(converters): preserve query param encoding in getQueryFromSearchParams - #1197

Open
azuma317 wants to merge 1 commit into
opennextjs:mainfrom
azuma317:fix/query-param-encoding-preserved
Open

fix(converters): preserve query param encoding in getQueryFromSearchParams#1197
azuma317 wants to merge 1 commit into
opennextjs:mainfrom
azuma317:fix/query-param-encoding-preserved

Conversation

@azuma317

Copy link
Copy Markdown

What

getQueryFromSearchParams parsed query values by iterating over URLSearchParams.entries(), which decodes each value. However, since #817, convertToQueryString intentionally does not re-encode values (it concatenates them raw to preserve their original encoding). This leaves the two sides of the round-trip inconsistent:

  • parse (getQueryFromSearchParams) → decoded values
  • serialize (convertToQueryString) → assumes already-encoded values, no re-encoding

As a result, any query value containing reserved characters (&, =, +, spaces, …) is corrupted when req.url is rebuilt from the parsed query in requestHandler.ts:

req.url =
  initialURL.pathname +
  convertToQueryString(routingResult.internalEvent.query);

A decoded value such as https://example.com/auth?a=1&b=2 gets concatenated as-is, so the embedded &/= split the outer query string and everything after the first reserved char is lost or reinterpreted as separate params.

Reproduction

This is the root cause of opennextjs/opennextjs-cloudflare#1134. It reliably breaks, for example, Sign in with Apple via better-auth's Expo flow: the client hits …/expo-authorization-proxy?authorizationURL=<percent-encoded URL>, the encoded %26/%3D inside authorizationURL are decoded to &/=, the URL is truncated at the first &, the state param is lost, and the endpoint returns 400 "Unexpected error". It only manifests in production (Workers / any deployment that goes through this req.url reconstruction), not in next dev.

Fix

Keep the parsed values percent-encoded so they round-trip correctly through convertToQueryString. This completes the model established in #817 (query records hold encoded values end-to-end) rather than reverting convertToQueryString — which would reintroduce the double-decode problem #817 fixed and break the existing "should respect existing query encoding" test.

Multi-value grouping and the empty-query case are preserved.

Tests

Added unit tests for getQueryFromSearchParams covering the empty case, single value, repeated-key grouping, and the reserved-character round-trip from #1134. Full unit suite (604 passed), biome check, and tsc --noEmit all pass.

…arams

getQueryFromSearchParams iterated over URLSearchParams.entries(), which
decodes the values. Since convertToQueryString does not re-encode them
(intentionally, since opennextjs#817), query values containing reserved characters
(&, =, +, spaces, ...) were corrupted when req.url was rebuilt from the
parsed query, silently truncating or mangling the query string.

Keep the values percent-encoded on parse so they round-trip correctly
through convertToQueryString.

Fixes opennextjs/opennextjs-cloudflare#1134
@changeset-bot

changeset-bot Bot commented Jul 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6cb2732

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@opennextjs/aws Patch
app-pages-router Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@opennextjs/aws@1197

commit: 6cb2732

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant