Skip to content

fix: keep the URL's query string when params are passed - #145

Merged
lesnik512 merged 1 commit into
mainfrom
fix/merge-url-query-params
Sep 28, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
fix/merge-url-query-params

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Problem

A URL's own query string was silently dropped whenever params were sent:

Call Before After
get("/items?a=1", params={"b": "2"}) ?b=2 ?a=1&b=2
get("/items?a=1&b=1", params={"b": "2"}) ?b=2 ?a=1&b=2
client params={"c": "3"}, get("/items?a=1") ?c=3 ?c=3&a=1
get("/items?a=1", params={}) no query ?a=1

The cause is upstream: httpx2.URL(url, params=...) replaces the query rather than merging, and httpx2.Client._merge_queryparams merges only client and request params. httpware passed params straight through.

Upstream status: pydantic/httpx2#905 is open, and fix PR pydantic/httpx2#966 has been open since 2026-05 without review. The original httpx maintainer treated the replace behaviour as intended (encode/httpx#3433). If #966 lands, this change still yields the same URL and the helper can be removed.

Fix

_merge_url_query folds the URL's query into params before handing off to httpx2. It is applied in build_request, the per-verb methods (_prepare_request) and stream, on both Client and AsyncClient.

  • Precedence: client params < URL query < per-request params; the per-request value wins on the same key.
  • Repeated URL keys (?a=1&a=2) are kept.
  • When the URL has no query, or no params are sent at all (per-request or client-level), the URL passes through untouched, so pass-through links such as pagination next keep their original encoding.

Behaviour change

  • Code relying on params replacing the URL's query now gets a merge.
  • When a merge does happen, httpx2 re-encodes the URL's query (e.g. a raw + may become %20); semantically equivalent.
  • Not addressed: a query inside base_url is still dropped by httpx2's _merge_url.

Tests

tests/test_url_query_merge.py: 44 cases across build_request, get and stream for both clients, written first (37 failed before the fix). Full suite, ruff, ty and eof-fixer pass locally.

httpx2 replaces a URL's query whenever params are sent, including
client-level params (pydantic/httpx2#905). Fold the URL query into
params in build_request, the per-verb methods and stream, for both
clients. Precedence: client params < URL query < per-request params.
@lesnik512
lesnik512 merged commit 8878a3d into main Sep 28, 2026
13 checks passed
@lesnik512
lesnik512 deleted the fix/merge-url-query-params branch September 28, 2026 20:16
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