fix(run_agent): skip OpenAI client rebuild on Anthropic-native sessions - #51508
Conversation
_replace_primary_openai_client was called unconditionally by all three
stream-cleanup paths in chat_completion_helpers (stale_stream_pool_cleanup,
stream_retry_pool_cleanup, stream_mid_tool_retry_pool_cleanup). When
api_mode=anthropic_messages the agent owns _anthropic_client and
self.client is None; _client_kwargs is an empty dict. The unconditional
OpenAI(**{}) call raised AuthenticationError with 'OPENAI_API_KEY not set'
even though the correct Anthropic key was present, producing a
deterministic false-alarm warning on every stream stall for profiles
without OPENAI_API_KEY.
Fix: short-circuit on api_mode=="anthropic_messages" and call
_rebuild_anthropic_client() instead, which handles both direct Anthropic
and Bedrock-hosted variants and honours _oauth_1m_beta_disabled.
The OpenAI path is unchanged; the guard only fires on the Anthropic wire.
Adds 4 regression tests covering: no OpenAI constructor call, correct
Anthropic rebuild invoked, failure logged under the right client name,
and the OpenAI path unaffected for non-Anthropic sessions.
Duplicate of #36719 — same guard in the same file ( |
|
Closed in favor of #53926, now merged to main (commit a0b9663). This was a popular bug — 12 independent PRs fixed the same issue (#28161): the three stream-cleanup paths in the streaming code rebuilt the OpenAI primary client unconditionally, which on Anthropic-native sessions both failed (no OPENAI_API_KEY) and left the wedged stream open, causing the ~15-minute hang. We salvaged @EloquentBrush0x's #28240 (the earliest dedicated fix for this issue) onto current main, resolved the conflict against the newer request-client cleanup helper, repointed the bug-encoding regression test, and added coverage for the two reachable Anthropic cleanup sites. Verified live: stale stream torn down in <1s instead of ~900s, Anthropic client closed+rebuilt, OpenAI rebuild never called on the Anthropic path. Thank you for the fix — closing as a duplicate of the merged work. Credit to everyone who reported and fixed this. |
What
_replace_primary_openai_clientwas called unconditionally by all three stream-cleanup paths inchat_completion_helpers(stale_stream_pool_cleanup,stream_retry_pool_cleanup,stream_mid_tool_retry_pool_cleanup). Whenapi_mode=anthropic_messagesthe agent owns_anthropic_clientandself.clientisNone;_client_kwargsis an empty dict. The unconditionalOpenAI(**{})call raisedAuthenticationErrorwith"The api_key client option must be set … OPENAI_API_KEY"even though the correct Anthropic key was present.This produced a deterministic false-alarm
WARNINGinerrors.logon every Anthropic stream stall for profiles that haveANTHROPIC_API_KEYbut noOPENAI_API_KEY— which is correct configuration. 3 occurrences verified on a single session.Root cause
All three cleanup sites call
agent._replace_primary_openai_client(). That method calls_create_openai_client(self._client_kwargs, ...)→OpenAI(**{}). The OpenAI SDK then reads the env forOPENAI_API_KEY, finds nothing, and raises. The exception is caught by theexcept Exception: passguard at each call site (so the agent continues), but thelogger.warninginside_replace_primary_openai_clientfires first and lands inerrors.logwith a misleading message.Fix
Short-circuit on
api_mode == "anthropic_messages"and call_rebuild_anthropic_client()instead — the existing method that already handles both direct Anthropic and Bedrock-hosted variants and honours_oauth_1m_beta_disabled. The OpenAI path is unchanged; the guard only fires on the Anthropic wire.Tests
4 new regression tests in
tests/run_agent/test_anthropic_stream_pool_cleanup.py:OpenAI()constructor never called for Anthropic-native sessions_rebuild_anthropic_clientis invoked insteadAnthropic client, notOpenAI client)api_mode=chat_completionsAll 4 pass; 19 adjacent lifecycle/switch-model tests unaffected.