fix(llm): the google adapter's real-transport guard — and the catch-gap it found live (#576 follow-up 4b) - #596
Merged
Conversation
…ap it found live (#576 follow-up 4b) The #576 review flagged the blind spot: the suite's factories INJECT httpx errors, so nothing could detect a future google-genai default-transport flip (the anthropic/openai 3.x SDKs already moved to httpx2; the genai sync client subclasses httpx.Client today and references httpx2 in the same module — the drift direction is live). The guard (3 tests): the adapter driven through the SDK's ACTUAL default transport — a real genai.Client against an unroutable endpoint (hermetic: a dummy key, no injected client, max_retries=0) on both the complete() and validate() ladders, plus the accept-then-close shape (a listener that resets mid-handshake — the #576 review's second case). The tests are structured to go RED and name the missing class the day a transport flip or an unwrapped exception shape arrives. THE GUARD FOUND A REAL GAP ON ITS FIRST RUN: a mid-connection reset surfaces as httpx.ReadError (or a raw httpcore error on the stream-read path) — a class the adapter's six-name catch list DID NOT include (ConnectError/ConnectTimeout/ReadTimeout/WriteTimeout/PoolTimeout/TimeoutException — ReadError and RemoteProtocolError were never listed). A real network blip mid-scan crashed untyped instead of mapping to LLMConnectionError. The fix: the clause becomes httpx.TransportError (the single base of every transport error in the httpx family — strictly wider than the old six, never narrower) + the httpcore bases for the raw path, on BOTH ladders. The identity split is documented in-line: httpx re-exports its OWN hierarchy (httpx.ReadError is NOT httpcore.ReadError), so both families can arrive; the guard tests keep the mapping honest across a backend flip. Evidence: pytest tests/test_llm_google_adapter.py — 11 passed (the conn-refused guard, the validate-path guard, the half-open guard — RED before the fix, GREEN after); the full Python suite — 4074 passed, 0 FAIL.
gadievron
requested review from
dgeyshis,
shahar-davidson and
sounil
as code owners
September 13, 2026 09:29
…nt class; the real chaining hierarchy), httpcore.TimeoutException added, the dependency declared, the docstring updated The reviewers' corrections: (1) the in-line rationale named a nonexistent httpcore.exceptions.TransportError and claimed every httpx transport error inherits the httpcore base — false (httpx chains via raise-from into its OWN hierarchy; neither family inherits the other; httpcore's roots are NetworkError/ProtocolError/TimeoutException). The comment now describes the real mechanism: httpx.TransportError is the load-bearing base (strictly wider than the old six); the httpcore bases are belt-and-braces for an un-wrapped raw exception. (2) httpcore.TimeoutException added to both clauses (the third root, absent — a raw httpcore timeout would have escaped untyped on the claimed raw path). (3) httpcore declared in pyproject.toml (it was transitive-only; the adapter now imports it directly — a module-level ImportError on the google provider would be far worse than the network blip this fixes). (4) the module docstring's stale two-name failure surface updated to the TransportError family.
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.
What
The google adapter's real-transport guard (#576 follow-up 4b) — and the live catch-gap it found on its first run.
The suite's factories inject httpx errors, so nothing could detect a future google-genai default-transport flip (the anthropic/openai 3.x SDKs already moved to httpx2; the genai sync client subclasses
httpx.Clienttoday and referenceshttpx2in the same module — the drift direction is live).The guard (3 tests)
The adapter driven through the SDK's actual default transport: a real
genai.Clientagainst an unroutable endpoint (hermetic — a dummy key, no injected client,max_retries=0), on both thecomplete()andvalidate()ladders, plus the accept-then-close shape (a listener that resets mid-handshake — the #576 review's second case). The tests are structured to go RED and name the missing class the day a transport flip or an unwrapped exception shape arrives.The live find
The guard found a real gap immediately: a mid-connection reset surfaces as
httpx.ReadError(or a raw httpcore error on the stream-read path) — a class the adapter's six-name catch list did not include (ReadErrorandRemoteProtocolErrorwere never listed). A real network blip mid-scan crashed untyped instead of mapping toLLMConnectionError. The fix: the clause becomeshttpx.TransportError— the single base of every transport error in the httpx family (strictly wider than the old six, never narrower) — plus the httpcore bases for the raw path, on both ladders. The identity split is documented in-line: httpx re-exports its own hierarchy (httpx.ReadErroris NOThttpcore.ReadError), so both families can arrive; the guard tests keep the mapping honest across a backend flip.Verification
pytest: the adapter suite — 11 passed (the conn-refused guard, the validate-path guard, the half-open guard — RED before the fix, GREEN after); the full Python suite — 4074 passed, 0 FAIL.