You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Findings: the front lane's rate limit is a size ceiling, and a session grows into it
Not a request for a fix. This is what a morning of failures measured, written down while the evidence is still in the log, plus what the six pull requests around it taught. The Owner decides what, if anything, to do with each.
The headline
Groq's free tier is 8,000 tokens per minute, and one Syrax turn is bigger than that. It is not a rate limit in any useful sense — waiting does not help, because a single request exceeds the whole minute's budget:
413 Request too large for model `openai/gpt-oss-120b` ... service tier `on_demand`
on tokens per minute (TPM): Limit 8000, Requested 8831
Measured across the morning: syrax-gemini/gemini-3.5-flash-lite took 313 calls with zero 429s. syrax-groq/openai/gpt-oss-120b took 62 calls with 13, every one of them a 413 dressed as a 429.
Finding 1 — a retry that grows cannot converge on a size limit
The runtime answers a 413 with rate-limit same-model retry 1/3 … delayMs=10000, then 20s, then 30s. Each retry appends the previous failure to the session, so each attempt is larger than the one that just failed:
firing
attempt 1
attempt 2
attempt 3
attempt 4
05:43
8631
8631
8659
8687
06:13
8715
8743
8771
8799
07:13
8939
8967
8995
9023
08:43
9275
9303
9331
9359
+28 tokens per retry, +112 per firing, monotonic for three hours. Backoff is the right answer to a rate limit and the wrong one to a size limit: the only thing that shrinks the request is not sending the failures back.
Finding 2 — a session grows by heartbeat, unprompted
Something pokes every chat on a 30-minute cadence (observed at :13:53 and :43:53, all four agents firing together) and each poke lands in the session as a turn. Counted in the trajectories:
Nothing in openclaw.json configures this, so it is a runtime default. It is the mechanism behind "my chat gets slower and then breaks": a session nobody has reset accumulates hundreds of turns that no one sent.
/new is currently the only lever, and its effect is dramatic — it is what stopped the runaway above at 08:49.
Finding 3 — context windows are not the constraint; the per-minute budget is
rung
context window
maxTokens
gemini-3.5-flash-lite
1,048,576
8192
ministral-3b-latest
131,072
1024
openai/gpt-oss-120b
131,072
1024
No turn is anywhere near a 131k context. The binding limit is Groq's 8k/minute, which is two orders of magnitude smaller than the window the same model advertises.
Unverified but worth checking: maxTokens: 1024 on two of the three front rungs may be its own bug. The shortlist tool call that #202 removed had eleven buttons carrying opaque tokens; a call like that could plausibly exceed a 1024-token output budget and arrive truncated, which is indistinguishable from the malformed calls seen. Nothing here measured it.
Finding 4 — the standing prompt is a floor under every turn
chat
AGENTS.md
rough tokens
tools
academic
5347 B
~1340
17
general
4533 B
~1130
10
system
3399 B
~850
7
media
1667 B
~420
4
Plus each tool's own schema and description, which for the search unit are long. ADR-0011 already says every line is paid on every turn; what is new is that against an 8k/minute ceiling the floor is a material fraction of the budget before the Owner has typed anything.
Finding 5 — a pin silently removes the fallback chain
/model syrax-groq/… collapses the lane to one candidate. The log records it plainly:
Offered as a workaround during an outage, it made things strictly worse: the pinned rung was the one with the 8k cap, and pinning removed the two rungs that would have answered. Nothing warns about this, in the chat or in /status.
Finding 6 — a provider finish_reason is not a failover reason
gemini-3.5-flash-lite aborting with function_call_filter: MALFORMED_FUNCTION_CALL produced failoverReason: null and no fallback decision at all — the chain was never consulted. openclaw classifies failover from transport signals (status codes, timeouts); a provider that returns 200 and then refuses in its finish reason falls outside that. Rate limits do route correctly (failoverReason: "rate_limit"), so this is a gap rather than a broken router.
A malformed call that fell through to the next rung would have been an annoyance instead of a morning-long outage.
What the six pull requests taught, which is not in any of them
An instruction change does not take effect in a running session. Three fixes looked like failures because the model was copying its own earlier tool calls out of the context window. In-context examples beat instructions, reliably. Every instruction change needs /new before it can be judged, and that is written down nowhere.
A test that scripts a tool call proves the surface, never the model.A shortlist's numbered list is dropped, because a text block loses to message #198 shipped an instruction describing one shape and a test scripting another; the test stayed green while the chat was broken. The scripted arguments must be the arguments the instruction asks for, or the test measures a shape nothing produces.
Prose is not a schema. Three wordings produced three different malformed calls. What fixed the schema errors was showing the literal JSON. What fixed the rest was not asking the model to build the structure at all (ADR-0033).
Findings: the front lane's rate limit is a size ceiling, and a session grows into it
Not a request for a fix. This is what a morning of failures measured, written down while the evidence is still in the log, plus what the six pull requests around it taught. The Owner decides what, if anything, to do with each.
The headline
Groq's free tier is 8,000 tokens per minute, and one Syrax turn is bigger than that. It is not a rate limit in any useful sense — waiting does not help, because a single request exceeds the whole minute's budget:
Measured across the morning:
syrax-gemini/gemini-3.5-flash-litetook 313 calls with zero 429s.syrax-groq/openai/gpt-oss-120btook 62 calls with 13, every one of them a 413 dressed as a 429.Finding 1 — a retry that grows cannot converge on a size limit
The runtime answers a 413 with
rate-limit same-model retry 1/3 … delayMs=10000, then 20s, then 30s. Each retry appends the previous failure to the session, so each attempt is larger than the one that just failed:+28 tokens per retry, +112 per firing, monotonic for three hours. Backoff is the right answer to a rate limit and the wrong one to a size limit: the only thing that shrinks the request is not sending the failures back.
Finding 2 — a session grows by heartbeat, unprompted
Something pokes every chat on a 30-minute cadence (observed at
:13:53and:43:53, all four agents firing together) and each poke lands in the session as a turn. Counted in the trajectories:Nothing in
openclaw.jsonconfigures this, so it is a runtime default. It is the mechanism behind "my chat gets slower and then breaks": a session nobody has reset accumulates hundreds of turns that no one sent./newis currently the only lever, and its effect is dramatic — it is what stopped the runaway above at 08:49.Finding 3 — context windows are not the constraint; the per-minute budget is
gemini-3.5-flash-liteministral-3b-latestopenai/gpt-oss-120bNo turn is anywhere near a 131k context. The binding limit is Groq's 8k/minute, which is two orders of magnitude smaller than the window the same model advertises.
Unverified but worth checking:
maxTokens: 1024on two of the three front rungs may be its own bug. The shortlist tool call that #202 removed had eleven buttons carrying opaque tokens; a call like that could plausibly exceed a 1024-token output budget and arrive truncated, which is indistinguishable from the malformed calls seen. Nothing here measured it.Finding 4 — the standing prompt is a floor under every turn
Plus each tool's own schema and description, which for the search unit are long. ADR-0011 already says every line is paid on every turn; what is new is that against an 8k/minute ceiling the floor is a material fraction of the budget before the Owner has typed anything.
Finding 5 — a pin silently removes the fallback chain
/model syrax-groq/…collapses the lane to one candidate. The log records it plainly:Offered as a workaround during an outage, it made things strictly worse: the pinned rung was the one with the 8k cap, and pinning removed the two rungs that would have answered. Nothing warns about this, in the chat or in
/status.Finding 6 — a provider
finish_reasonis not a failover reasongemini-3.5-flash-liteaborting withfunction_call_filter: MALFORMED_FUNCTION_CALLproducedfailoverReason: nulland no fallback decision at all — the chain was never consulted. openclaw classifies failover from transport signals (status codes, timeouts); a provider that returns 200 and then refuses in its finish reason falls outside that. Rate limits do route correctly (failoverReason: "rate_limit"), so this is a gap rather than a broken router.A malformed call that fell through to the next rung would have been an annoyance instead of a morning-long outage.
What the six pull requests taught, which is not in any of them
/newbefore it can be judged, and that is written down nowhere.textblock loses tomessage#198 shipped an instruction describing one shape and a test scripting another; the test stayed green while the chat was broken. The scripted arguments must be the arguments the instruction asks for, or the test measures a shape nothing produces.mainindefinitely — The stand-down gateway test reads an already-read deployment #196 was exactly that, and CI never sees it.Open questions for triage
maxTokens: 1024correct for the two rungs that carry it?