Budget your retries per turn
The turn died about forty minutes in, right after I typed a correction into the running agent. No provider flake, no network error, nothing in the logs that looked like an upstream fault — just a hard stop the moment my steer message landed. The status was loop_error, which told me exactly nothing. I assumed I had burned through my provider retries and started rearranging the wrong numbers for an hour.
What I actually found was two counters wearing one name. One of them is cheap and resets itself. The other one was quietly counting my own messages.
Fallback is where the counter gets wiped, not where it gets spent
My working theory had been that every failover jump costs a retry, so a small retry budget would snap the fallback chain shut after a couple of hops. The Hermes docs on fallback providers say something different, and it took me a while to read it without my assumption filtering the sentence.
Fallback is turn-scoped. Each new user message starts over with the primary model, and inside a single turn fallback fires at most once. And the failover jump itself resets the retry counter and continues the conversation. So a failover hop isn't a debit from the retry budget — it's the point where the counter is cleared. The thing that governs provider attempts before fallback is agent.api_max_retries, which defaults to 3, meaning four attempts. The configuration docs recommend setting it to 0 if you want fast failover rather than polite patience with a sick provider.
That reframed the whole problem. If failover doesn't consume retries, then "my retry budget is too small" can't explain a chain breaking after a couple of jumps. Something else was spending, and it wasn't the provider.
The counter my correction was spending
The conflation is documented: issue #128000, opened 2026-09-29. A redirect/rebuild restart cap was using the same limit as api_max_retries. Mid-turn user messages — steer instructions, clarify answers — counted against that cap. Accumulate enough of them in one turn and the turn terminated with redirect_restart_limit_exceeded, which the outside world sees as loop_error.
So the ceiling named for provider retries was also the ceiling for context rebuilds, and also the ceiling for how many times I was allowed to talk to my own agent while it was working. That last part is the one that bit me, because it's the part nobody would guess. I had been reasoning about it as a network weather problem when it was a bookkeeping problem.
Suppose a turn needs three context rebuilds and you also send two steering corrections. That's five hits against a ceiling you may have set to the same value you chose for api_max_retries, and if you chose that value low to get snappy failover, you also made yourself quiet mid-turn. My incident was one steer message too many, not one provider too many.
The fix landed as PR #130528, merged and the issue closed 2026-10-01: a reply now resets the redirect/rebuild restart cap, and mid-turn corrections no longer end the turn. If you're on anything older, your steering messages are still being charged to a counter that has nothing to do with them, and the symptom is a loop_error that looks like infrastructure.
Reload has to drain, or it kills the thing you're fixing
The other half of my afternoon was the reload path. I had been reloading the agent process whenever I shipped a config change, and I had not thought hard about what "reload" means when a turn is mid-flight. A restart in that moment doesn't pause the work, it destroys it. Whatever the turn was carrying — partial tool results, half a plan, a pending approval — is gone, and the caller sees a crash rather than a reload. If you're already spending budget recovering from that crash, you've paid twice for a config change.
Drain-aware reload is a different shape. The process takes the signal, stops accepting new work, lets the running turn reach its end, and only then comes back up. From the outside it looks like a pause instead of an outage. It's more code than systemctl restart, and it's the difference between a hotfix and a second incident.
I ended up treating the two as separate controls on purpose. A retry is a provider-level thing that a failover hop clears. A restart is a lifecycle thing that a reply, after the fix, also clears. Wiring them to the same integer was the bug, and it was a readable bug — the number looked correct in every file it appeared in.
The backstop that decides whether you actually give up
Once retries are exhausted and the fallback chain is done, you're not at the end of the machinery. agent.auto_recovery_cycles sits above that, default 5, and it runs wait-and-retry cycles with a plan of 15, 30, 60, 60, 60 seconds. This is the layer that answers the real question — does my agent abandon the task on a bad provider day, or does it keep breathing while the upstream recovers.
So "too small breaks the chain" is true, but it's not the retry budget that breaks under normal load. A chain that dies after a couple of hops is a sign that a restart cap is being spent by something that isn't a restart, and that auto-recovery either never got its turn or was set to nothing. I now read the two as layers rather than as one dial: retries and failover decide how fast you leave a broken provider, auto-recovery decides how long you stay alive afterwards.
What the dashboard doesn't tell you
The lesson I keep coming back to is that loop_error is a lie by omission. It tells you the loop ended, not which counter ended it. When I see it now, I check three things in order: was this a provider attempt (api_max_retries), a redirect or rebuild restart, or a mid-turn message charged to the wrong cap.
And I check which version I'm running before I touch any of it. The specific wastefulness — steering messages eating a restart budget — is fixed, and the fix is the kind that makes a bad configuration look like a good one until you read the number and realize what it was really counting.