fix(python-client): fail loud when a stream opens on a redirect - #4173
fix(python-client): fail loud when a stream opens on a redirect#4173ghoshp83 wants to merge 1 commit into
Conversation
The SDK's httpx.AsyncClient uses httpx's default follow_redirects=False, and the stream-open guard only rejected status >= 400. A proxy or gateway answering a 3xx (e.g. 302) on the stream request therefore fell through to the SSE parser, which found no data: frames in the redirect body and yielded nothing — handing the caller a silent, error-free, zero-event stream instead of surfacing the connection that never reached the server. _stream_session_events already documents ":raises OmnigentError: If the server returns a non-2xx status", so this is the redirect half of a contract the rest of the sessions namespace keeps. Reject a 3xx on the stream open at both sites — SessionsNamespace.stream (via _stream_session_events) and the deprecated /v1/responses stream — raising OmnigentError rather than decoding an empty stream. Adds a regression test that returns a 302 from an httpx.MockTransport and asserts the open raises instead of completing with zero events; it fails against the old >= 400 guard. Spotted by @bdchatham while comparing this client against the Go client during review of omnigent-ai#4010. Signed-off-by: ghoshp83 <pralay.ghosh@gmail.com>
941f09e to
669f9d6
Compare
|
@ghoshp83 Thanks for the PR! It doesn't reference an issue yet. We require an issue for every PR, so the work can be prioritized before it's reviewed. Add one to the description:
No issue exists for this yet? Open one first, then reference it. That's how we track what's worth doing, and it's usually quicker than it sounds. Note a reference has to point at an issue: naming another PR doesn't count. The only exceptions are changes with no user-visible behaviour: pure Refactor / chore, Docs, or Test / CI work. If that's genuinely what this is, check that box under Type of change. Anything that fixes a bug, adds a feature, or changes the UI needs an issue, even when it also touches docs or tests. See CONTRIBUTING.md for the full policy. No action is taken beyond this comment. |
Closes #4226
What
When opening a stream, the client silently swallows a redirect. Its
httpx.AsyncClientis built with httpx's defaultfollow_redirects=False, and the stream-open guard only rejectedstatus >= 400:So a proxy or gateway answering a 3xx (e.g.
302) on the stream request is neither followed nor rejected — it falls through to the SSE parser, which finds nodata:frames in the redirect body and yields nothing. The caller gets a silent, error-free, zero-event stream and cannot tell it apart from "the session produced nothing"._stream_session_eventsalready documents:raises OmnigentError: If the server returns a non-2xx status, so this is the redirect half of a contract the rest of the sessions namespace already keeps.Fix
Reject a 3xx on the stream open at both sites —
SessionsNamespace.stream(via_stream_session_events) and the deprecated/v1/responsesstream — raisingOmnigentErrorinstead of decoding an empty stream.raise_for_statusis a no-op below 400, so the redirect case needs its own arm. Redirects stay disabled rather than followed, keeping the client's credential-safety posture intact.Test
tests/frontends/sdk/test_stream_redirect_guard.pydrives_stream_session_eventswith anhttpx.MockTransportthat returns a302, and asserts the open raisesOmnigentError(status302) and yields zero events. It fails against the old>= 400guard (which completes empty, no error) and passes with the fix.Not in this PR
While here,
_CODE_MAPin_errors.pyis built insideraise_for_statusbut never read (classification is by status code) — it looks like dead code. Left out to keep this change focused; happy to send a separate cleanup.Credit
Spotted by @bdchatham while comparing this client against the Go client during review of #4010.