OmniRoute Version
3.8.52 (also reproduced on a fresh reinstall; resolved by downgrading to 3.8.51)
Provider(s) Involved
claude (BYOK via local gateway)
Model(s) Involved
cc/claude-opus-5-5 and cc/claude-fable-5-1 (both affected). cx/gpt-6.1-sol, cx/gpt-6-luna, and other non-Anthropic models on the same install were unaffected throughout.
Client Tool
Prime Agent (prime-agent) 0.10.0, Rust build, extended thinking enabled, long multi-turn autonomous session (22k+ stored events), openai-completions API shape proxied to Anthropic.
Description
This looks like the combination of the fix in #15488 (merged as #15505) and the separate, known-distinct error class named in #7899.
#15505 made the empty-stream detector in shouldAbortEmptyClaudeStream() exempt end_turn/stop_sequence, but explicitly kept content_filter (and missing stop_reason) as a treated-as-failure case. In practice, on a long-running extended-thinking session, a turn occasionally ends with stop_reason: content_filter on completely ordinary engineering/security-review content (nothing resembling actual policy-violating material). That now surfaces to the client as an upstream failure with empty content:
"diagnostics":[{"type":"provider_stream_failure","error":{"message":"Provider finish_reason: content_filter"}}]The client then retries by resending conversation history, including the thinking block from that content_filter-truncated turn. Since OmniRoute never delivered that turn's thinking block intact to begin with, Anthropic rejects the resend with the separate error class #7899 explicitly calls out as out of its own scope:
400 [400]: messages.N.content.M: `thinking` or `redacted_thinking` blocks in the latest assistant message
cannot be modified. These blocks must remain as they were in the original response.From here the session is permanently wedged: every retry resends the same now-malformed history and gets the same 400, in a loop (observed recurring every ~40s across many hours). The only way out is forking the session to a point before the content_filter turn and continuing on a non-Anthropic model, or downgrading OmniRoute.
Steps to reproduce
- Run OmniRoute 3.8.52 with an Anthropic model (
claude-opus-5-5orclaude-fable-5-1) and extended thinking enabled. - Drive a long, multi-turn autonomous coding session (large accumulated tool-use history) until a turn's
stop_reasoncomes backcontent_filteron ordinary content (we could not force this on demand; it surfaced naturally over ~9 days of a continual session, and also reproduced within minutes of a fresh fork of that session's history). - Observe the
Provider finish_reason: content_filterfailure, then the subsequentthinking blocks ... cannot be modified400 on the very next turn, which then repeats indefinitely.
Expected behavior
content_filtershould not be treated identically to a dropped/empty stream when the upstream payload was otherwise seen (mirroring theend_turn/stop_sequenceexemption from #15505) — or at minimum, when the client retries after acontent_filter-truncated turn, OmniRoute should drop/sanitize that incomplete turn's thinking block from the replayed history rather than resending it as-is, the same way #7899 asks for completed-turn thinking removal on signature rejection.
Workaround
Downgrading the global install from omniroute@3.8.52 to omniroute@3.8.51 resolved it immediately for the same client/session/model combination. Non-Anthropic models (cx/gpt-6.1-sol, cx/gpt-6-luna) were never affected on either version, pointing at the Claude-specific empty/filtered-stream handling path specifically.
Related
- #15488 / #15505 (content_filter explicitly left as a treated-as-failure case)
- #7899 (names "the separate 'latest assistant message cannot be modified' error" as explicitly out of its own scope — this report is that error)
Activity
0No activity yet.