fix!: append params after the URL's own query and keep its bytes - #156
Merged
Merged
Conversation
This was referenced Oct 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes how a URL's own query string combines with
params, to matchrequestsand the pending upstream fix (pydantic/httpx2#966). It also stops httpware from re-encoding that query string.Behaviour change
params(client-level, then per-request) are now appended after the URL's query. Duplicate keys are kept instead of overridden.?a=1&b=1b=2a=1&b=2a=1&b=1&b=2?a=1&c=1b=2c=3c=1&a=1&b=2a=1&c=1&c=3&b=2?a=1c=3c=3&a=1a=1&c=3Code that put a key in the URL and relied on
paramsto replace it will now send the key twice. Release notes should call this out.Bug fixed: the URL's query was re-encoded
Before, when any
paramsapplied, the URL's query was parsed and rebuilt:?q=a%20bwent out asq=a+band?flagasflag=. That can break signed URLs and servers that are strict about encoding. Now the original bytes are sent unchanged (new test caseurl-query-bytes-kept).How
httpx2 sends
paramsthroughstr(QueryParams(...)), which re-encodes them, so nothing passed asparamskeeps the URL's original bytes._build_requestreplaces_merge_url_query. It strips the URL's query, lets httpx2 build the request, then puts the original query bytes back in front of whatever query httpx2 built.To route the uncapped
stream()path through_build_request, it now uses_stream/_stream_async. These mirrorhttpx2.Client.stream:build_request, thensend(stream=True), then close on exit. The capped path already worked this way. The request is still built lazily, inside the exception mapper.Relation to #146
tests/test_url_query_merge.pynow matches upstream behaviour. Against pydantic/httpx2#966 (2631c38) with_build_requeststubbed to a pass-through, all 50 tests pass. Against httpx2 2.13.1 with the same stub, 43 fail, so the workaround is still needed until a release includes the fix. Once one does, #146 is a plain deletion with no behaviour change.Verification
just lint-cipasses, andjust test-cipasses 1007 tests at 100% coverage.