Repository navigation
Closed
Labels
P1High — major feature broken, no workaroundHigh — major feature broken, no workaroundcomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointprovider/anthropicAnthropic native Messages APIAnthropic native Messages APItype/bugSomething isn't workingSomething isn't working
Description
Bug Description
Two interacting defaults turn an Anthropic 429 (soft rate limit) into a hard outage:
agent/anthropic_adapter.pybuild_anthropic_client(andbuild_anthropic_bedrock_client) constructAnthropic(...)/AnthropicBedrock(...)without settingmax_retries, so the SDK's defaultmax_retries=2kicks in. The SDK retries 429/5xx with a 1-2s exponential backoff and ignoresRetry-After.run_agent.py:14462capsRetry-Afterat 120s:_retry_after = min(float(_ra_raw), 120) # Cap at 2 minutes. On Anthropic Tier 1 accounts the input-token bucket resets in ~171s.
Net effect on a rate-limited account:
- SDK fires 2 silent retries inside hermes's outer retry loop → burns through 2-3 request slots against a bucket that won't refill for minutes
- Outer hermes retry sleeps 120s (capped) and fires again before the actual reset window → another nested SDK retry burst
- Soft per-minute throttle escalates into hard request-count exhaustion; the agent appears hung
Steps to Reproduce
- Anthropic API key on Tier 1 (or any account where reset window > 120s)
- Saturate input-token budget (long conversation / many parallel turns)
- Receive
429withRetry-After: 171(or any value > 120) - Observe: SDK retries fire silently inside hermes's retry, hermes sleeps 120s instead of 171s, request slots exhaust before the actual reset window
Expected Behavior
- Single retry strategy: hermes's outer loop owns rate-limit backoff (it already reads
Retry-After) Retry-Afterhonored up to a sane ceiling (e.g. 600s — covers all realistic provider windows while still rejecting absurd values)
Actual Behavior
- Two retry layers fighting each other
- 120s cap turns a 171s reset window into a death loop
Affected Component
- Agent Core (conversation loop)
Root Cause Analysis
Verified on origin/main HEAD 77276070f5 (2026-05-15):
agent/anthropic_adapter.pybuild_anthropic_client— nomax_retrieskwarg passed toAnthropic(**kwargs)→ SDK default 2agent/anthropic_adapter.pybuild_anthropic_bedrock_client— nomax_retrieskwarg passed toAnthropicBedrock(...)→ SDK default 2run_agent.py:14462—_retry_after = min(float(_ra_raw), 120)
Proposed Fix
- Set
max_retries=0on both SDK constructors (Anthropic + AnthropicBedrock) — delegate retries to hermes's outer loop entirely. The outer loop already honorsRetry-After; the SDK layer just doubles up. - Raise the
Retry-Aftercap from 120s to 600s (covers Anthropic Tier 1 and similar providers; still rejects pathological hours/days values).
I have working commits in my fork that survived rebase onto v2026.5.7 (tests included). Happy to open a PR — wanted to surface this first since max_retries=0 is a behavior change for any user relying on SDK-level retry semantics.
Environment
- Hermes: v2026.5.7 base + verified against
origin/main2026-05-15 - Python: 3.11+
- OS: macOS 15.x (bug is platform-independent — pure provider retry logic)
Metadata
Metadata
Assignees
Labels
P1High — major feature broken, no workaroundHigh — major feature broken, no workaroundcomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointprovider/anthropicAnthropic native Messages APIAnthropic native Messages APItype/bugSomething isn't workingSomething isn't working
Activity
flowioo commentedon May 15, 2026
We have observed a related issue with custom providers using Anthropic transport. Beyond the Anthropic-specific SDK retry described here, run_agent.py has a broader 429 handling problem:
On custom Anthropic-compatible providers, the 429 retry logic eagerly falls back to the next provider instead of retrying with jittered backoff on the original. With a single primary provider, this creates a loop: 429 -> try fallback (may not exist) -> fail -> retry primary -> 429 again.
We patched run_agent.py to use jittered exponential backoff (base 5s, max 60s) on the original provider before falling back. Combined with the SDK fix (max_retries=0 to prevent double-retry), this resolves the deadlock for all Anthropic-compatible providers.
For Retry-After cap: suggest 600s instead of 120s. Tier 1 reset windows are ~171s, so 120s cap causes death loops.
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
teknium1 commentedon Jun 28, 2026
Fixed on
mainvia PR #53918 (rebase-merge, your commit1ab35ba25preserved with your authorship).Both halves of your diagnosis landed:
max_retries=0on all three Anthropic SDK constructors (Anthropic,AnthropicBedrock, bearer-hook client) — the outer loop owns retry and honorsRetry-After.Retry-Aftercap raised 120s → 600s so Tier-1 reset windows (~171s) are honored instead of re-tripping the limit.During the salvage we audited the same bug class on the OpenAI/aggregator path and found it there too (SDK default
max_retries=2, never overridden), so we also setmax_retries=0at the singlecreate_openai_clientchokepoint.auxiliary_clientis intentionally left alone — it builds its own clients and isn't wrapped by the conversation loop.Thanks for the precise root-cause writeup — line-level references on
mainmade this an easy verify-and-ship.#53918
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
6 remaining items
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
fix(retry): honour HTTP-date Retry-After and clamp negative values (f…
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…
fix(anthropic): stop SDK auto-retry double-firing and raise Retry-Aft…
fix(agent): disable OpenAI SDK auto-retry that double-fires inside th…