Repository navigation
ctx_expand recovery loop triggers Anthropic reasoning_extraction refusal on Opus 5.5 #700
Description
Short description
Repeated ctx_expand calls to "recover / piece together" prior messages trips provider content-filter (reasoning_extraction) on claude-opus-5-5
What happened?
Expected: Agent can call ctx_expand a few times to rebuild context and continue working.
Observed: After ~6-7 consecutive ctx_expand calls in one session, the next
model turn fails hard with:
Provider blocked the response (reasoning_extraction): This request was blocked
as it seems to violate Anthropic's Terms of Service restrictions on reverse
engineering or duplicating model outputs.
Session ends with idle outcome: failed / provider.content-filter. Retry on the
same session re-fails. Fresh session + Sonnet works.
Repro pattern (from assistant reasoning + tool history):
- Long review session on Opus 5.5 (custom orchestrator agent,
anthropic / claude-opus-5-5 / maxvariant) - Agent enters a loop like:
- reasoning: "I should keep recovering the context, so I'll expand messages 19 and 20…"
- reasoning: "Since session history is empty, I need to check messages 17 onward to piece together where things left off"
- reasoning: "I still need to recover messages 11-20…"
ctx_expand { message: 11 },ctx_expand { start: 11, end: 14 },ctx_expand { message: 6 }, …
- After ~7 of these, provider returns
stop_reason: refusal / reasoning_extraction.
Shell / read / edit calls just before the block were benign (git status/diff, log grep, test file read) — the only suspicious shape is the ctx_expand + "recover / expand / full recovery / piece together prior outputs" wording, which matches Anthropic's documented trigger for this category.
Questions for maintainers:
- Is there a recommended cap / backoff for consecutive
ctx_expandcalls to avoid looking like "duplicating model outputs"? - Should
ctx_expandoutput avoid re-emitting priorreasoningblocks verbatim on Opus-class models? - Is per-model tool gating planned (e.g. deny
ctx_expandonly on Opus)? Today permissions are only global or per-agent.
Diagnostics
Plugin version: 0.49.6
OpenCode version: v2.0.31 (service 2.0.27)
Platform: linux x64 (AlmaLinux 8.10)
Client: OpenCode (auto-detected)
Harness: opencodePlugin version
0.49.6
OpenCode version
2.0.31
Platform
linux x64
Client
OpenCode Web
Log output (optional)
[magic-context] registered slash commands: ctx-status ctx-recomp ctx-dream ctx-flush ctx-embed ctx-wrapup
[magic-context] @cortexkit/opencode-magic-context v2 TUI setup
[magic-context] clear_reasoning_age is deprecated and ignored. Magic Context now keeps reasoning up to a token budget, keep_reasoning_tokens (default 10,000).
[opencode server] Failed to drain Session — Session.StepFailedError: Provider blocked the response (reasoning_extraction):
This request was blocked as it seems to violate Anthropic's Terms of Service restrictions on reverse engineering
or duplicating model outputs. idle outcome: failed / provider.content-filter
Model: claude-opus-5-5 (anthropic, max variant)
Preceding turns: ~7x ctx_expand (message / start-end) + reasoning about "recovering context / piecing together earlier messages"
Activity
magic-alfonso commentedon Oct 16, 2026
Thanks for the detailed report. Before deciding on any change, two facts from the source and a few questions.
What
ctx_expandreturns: recovering a single message (ctx_expand(message=N)) and the default range view return the message's text and tool inputs/outputs only. Reasoning and thinking blocks are skipped entirely (packages/plugin/src/tools/ctx-expand/render.ts,renderPartFull). Only the opt-inverbose=truelisting shows reasoning at all, as a 120-character preview per part. So the expanded output shouldn't re-emit earlier reasoning verbatim, though it does return the assistant's earlier text replies.The part that looks wrong to us is the loop itself: the model says "session history is empty" and keeps expanding messages 6 to 20. In a healthy session the older turns are summarized in a
<session-history>block at the start of the conversation, and the newest messages are already visible, so there should be nothing to piece together. We'd like to find out why that didn't hold here, especially since OpenCode 2.0.31 is newer than the versions we've tested.Could you share:
npx @cortexkit/magic-context@latest doctor --issuefrom that machine.ctx_expandcalls returned. Did any say a message was missing, or that the range is the live tail and already visible?/ctx-wrapupor/ctx-recomp, or been compacted, shortly before.We're checking the same path on OpenCode 2.0.31 in the meantime.
procodingtools commentedon Oct 16, 2026
Thanks — helpful to know reasoning is skipped in renderPartFull.
doctor: plugin 0.49.6, OpenCode v2.0.31, linux x64, OpenCode auto-detected.
Raw
doctor --issuelists machine-local project identities so I don'twant to paste it verbatim — tell me which section helps and I'll redact.
The expands succeeded, none said missing/already-visible. Example:
ctx_expand { message: 11 }→[11] A (assistant) — full recovery:raw bodies contain project file lists.
56 stored messages (seq 5–582), ~90min session, no compaction timestamp,
no /ctx-wrapup or /ctx-recomp immediately before. Loop was the last
thing before the refusal.
Orchestrator prompt does not mention recover/reconstruct/ctx_expand
(213 lines, grepped). The "keep recovering context / piece together"
phrasing is model-generated.
So the refusal seems to be on the request shape (repeated expand +
"recover prior outputs" intent on Opus 5.5 max), not on reasoning
leaking through ctx_expand output. Happy to test a cap/backoff or a
"stop after N expands" guard if you ship one.
magic-alfonso commentedon Oct 16, 2026
Correcting my earlier reply: the question I asked can't be answered from the session view, because placeholders only exist in what the model is sent; the conversation you see always shows the original messages. Please ignore it.
Thanks, that narrows it down a lot. You don't need to paste the doctor output. We're also checking what
doctor --issueincludes about local projects, so reports can be shared safely.A 56-message session with no compaction is young enough that it may not have a
<session-history>summary yet. Our current theory: some of the early messages had been dropped from what the model sees, to save space, and the model tried to rebuild what was behind them, which is what thectx_expandguidance says the tool is for.What would confirm it, with no message content: the output of
/ctx-statusin that session (or a new message in it, if it's still open). It shows the number of history summaries and drops. We're reproducing the same session shape on OpenCode 2.0.31 and will follow up here.magic-alfonso commentedon Oct 16, 2026
Correcting my earlier reply: the fix changes how this cleanup measures age, not when it is allowed to run. Details below.
Thanks, that rules out the compaction theory, and it helped us find a likely cause on our side.
Magic Context has an automatic cleanup that drops older tool outputs whenever the prompt cache is rebuilt anyway. It counts an output as old once it has survived one rebuild. The problem is that a rebuild also happens when the cache simply expires after a few idle minutes, or when you switch models. So two such rebuilds can remove most earlier tool outputs from a young session, even though the context is nowhere near full. The model then sees an empty history block (no summary exists yet in a young session) plus placeholders where its earlier work was, and tries to get it back with
ctx_expand. We reproduced the retrieval side on a real OpenCode 2.0.31 host: seven expands in a row, including the same message four times, all succeed.ctx_expanddoesn't return earlier reasoning blocks;verbose=trueshows at most a 120-character preview of each.A fix is in progress so that an output only counts as old once the conversation has actually moved on past it, not because idle time or a model switch triggered extra rebuilds. Three quick answers would confirm this is what hit you:
While reproducing this we also found a separate bug: asking
ctx_expandfor a message number past the end of a session returns the first message under that number. A fix for that is ready and will ship in the next release.magic-alfonso commentedon Oct 17, 2026
Released in v0.49.9.
The root cause on our side was age-based cleanup of tool outputs. It counted cache rebuilds, so in a young session two idle gaps of five minutes, or a model switch, could clear outputs the current task still needed, and the model then tried to recover them with
ctx_expand. Now a tool output only counts as old after three newer user turns, or once newer content fills 25% of the context window. Background notices and injected history don't count as turns. This applies on OpenCode 1 and 2, Pi, Oh My Pi and Rust mode, and cleanup still only happens on a request that already rebuilds the cache.0.49.8 also fixed
ctx_expandon OpenCode 2 returning the session's first message for an ordinal past the end. If you still see a recovery loop on 0.49.9, please reopen with the output of/ctx-statusfrom that session.