Skip to content

fix(llm): the google adapter's real-transport guard — and the catch-gap it found live (#576 follow-up 4b) - #596

Merged
gadievron merged 2 commits into
masterfrom
fix/google-real-transport-guard
Sep 13, 2026
Merged

fix(llm): the google adapter's real-transport guard — and the catch-gap it found live (#576 follow-up 4b)#596
gadievron merged 2 commits into
masterfrom
fix/google-real-transport-guard

Conversation

@gadievron

Copy link
Copy Markdown
Collaborator

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.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 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 (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) — 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.ReadError is NOT httpcore.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.

…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.
…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.
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