Repository navigation
[Bug]: OpenCode thread permanently wedged by "Insufficient context allowance" after a failed turn; /compact cannot recover it #19875
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- On the OpenCode provider, run a long thread on one native session until T3 has stored a few hundred KB of history for it. Command output counts toward this. Here it was ~354 KB across 21 runs.
- Let one turn fail before the provider accepts it (
ProviderAdapterTurnStartError, "The provider could not start this turn"). Here this followed a turn whose end could not be recorded (see Logs). - Send any further message, for example "continue".
- Run
/compact, then send another message. - Switch the model (opus-5-5 → fable-5-1, both via opencode) and send again.
No provider or instance switch is involved; this is the same opencode instance and the same native session throughout.
Expected behavior
- A ~1 KB replay of a few failed user messages should fit, or be dropped or deferred instead of failing the turn.
/compact, as the error message suggests, should let the next turn proceed.
Actual behavior
Every turn after step 2 fails within ~250 ms with:
Insufficient context allowance for the provider handoff. Compact the target conversation or use a larger-context model; the current request has not been truncated.
/compact completes, but the next turn fails the same way. Switching the model doesn't help either. The thread can't be recovered in the app: each failed message becomes another missed run, so the loop feeds itself.
Root cause, traced through statev2.sqlite, server.trace.ndjson* and the bundled server code:
- Failed runs that never reached the provider become missed runs. A failed run with no provider turn is added to
missedRuns, so the next turn builds adelta_since_target_last_seenhandoff to replay it. Here that was runs 15, 16, 18–21, about 1 KB in total: user messages plus the error items. - The handoff budget is 0 because opencode reports no usage. The opencode provider turns report
turnTokenUsage.usageStatus: "unavailable". As a resultlatestNativeContextUsagefinds nothing andusedTokensis undefined.handoffBudgetthen usesnativeContextEstimate, which is the byte length of all stored T3 history for that native thread. That came to ~354 KB here, of which command output was 274 KB, and it is counted as if each byte were a token:This is 0 for any window under about 472k. At 0, not even the coverage text fits, somax(0, min(16000, window − nativeEstimate − msg − max(16k, window/4)))deliverContextHandoffsthrowsContextHandoffBudgetError. - Compaction can't move either input.
/compactruns withdeferInline, so it skips the handoff and succeeds. It doesn't clearmissedRuns, and it doesn't changenativeContextEstimate, because that comes from T3's projection, not from the compacted native session. The compaction turn also reports usage asunavailable.
The advice in the error message ("Compact the target conversation") therefore cannot work for opencode threads.
Impact
Blocks work completely
Version or commit
0.0.46-nightly.20261015.3145
Environment
macOS 26.6.2 (arm64), T3 Code (Nightly) desktop app, opencode 1.19.0, models anthropic/claude-opus-5-5 and anthropic/claude-fable-5-1 through opencode, full-access runtime
Logs or stack traces
# Trigger: the end of the preceding long turn (run 14) could not be recorded
2026-10-15T11:36:34Z orchestrationV2.EventSink.write Failure
ProjectionStoreApplyEventError: Failed to apply orchestration projection event provider-turn.updated.
[cause]: SqlError/UniqueViolation: UNIQUE constraint failed:
orchestration_v2_projection_provider_turns.provider_thread_id, orchestration_v2_projection_provider_turns.ordinal
# -> provider turns for runs 12 and 14 are still status=running, completedAt=null, turnTokenUsage=null
# Run 15: ProviderAdapterTurnStartError ("The provider could not start this turn")
# Runs 16, 18, 19, 20, 21 (11:37:19 to 11:39:26Z), all in ~200 ms:
orchestrationV2.deliverContextHandoffs Failure
ContextHandoffBudgetError: Insufficient context allowance for the provider handoff. ...
at orchestrationV2.deliverContextHandoffs (Nightly)
# Run 17 (/compact) completed in between; the provider turn has turnTokenUsage.usageStatus = "unavailable"
# Projection state for the thread
provider_turns (ordinal, status, run):
9 running run 12 (run failed)
10 completed run 13 usageStatus=unavailable
11 running run 14 (run failed; this is the update that hit the unique violation)
12 completed run 17 (/compact) usageStatus=unavailable
runs 15, 16, 18-21: status=failed, no provider turn -> missedRuns
provider_threads.contextUsage = null
Sum of historicalMessage() bytes for the thread: 354,570 (command_execution 274,308; assistant_message 77,026)Suggested fixes (independent of each other):
- Don't throw for an optional replay. When the only pending handoff is a
delta_since_target_last_seenof failed runs that never reached the provider, drop or defer it (or send only the coverage pointer tot3_thread_read) rather than failing the turn. - Reset or ignore the estimate after compaction. After a successful
compactThreadon a native thread, stop using byte-summed T3 history as the occupancy estimate, or treat it as 0 until real usage is reported. - Use opencode's real numbers. Map opencode token usage to
usedTokens/maxTokens, or at least don't count raw bytes as tokens. - Fix the ordinal collision. Investigate the
provider_turns (provider_thread_id, ordinal)unique violation, which leaves turns stuck asrunning.
Related: #15997 (same wedge, triggered by an Antigravity instance switch) and #12931 (Pi; the same 128k-default and byte-estimate budget collapse).
Workaround
Fork the thread. The new native session starts with nativeContextEstimate = 0, so the budget is the full 16,000 and the handoff succeeds. T3CODE_CONTEXT_HANDOFF_TOKEN_CAP does not help, because the budget is clamped to 0 by the window calculation before the cap applies.
Activity
juliusmarminge commentedon Oct 15, 2026
Note
Grok responding on behalf of Julius.
Thanks for the detailed trace. The wedge itself is what open PR #19414 fixes: an oversized context handoff is deferred instead of raising
ContextHandoffBudgetError, so the turn proceeds and the handoff is retried once it fits. I've linked this issue to that PR so it closes when it lands. #16147 is the same failure on OpenCode with a different trigger (unknown model window after adelegate_taskresult).The budget being 0 because OpenCode reports usage as
unavailable(so the byte-length estimate stands in for tokens) is a separate accuracy problem. If the thread still misbehaves once #19414 is in a nightly, please comment here and we'll reopen that part.lageorgem commentedon Oct 15, 2026
@juliusmarminge
Update: the trigger is resuming an opencode subagent, and it reproduces in a fresh fork
I forked the thread to recover it. The fork failed the same way within its first turn. Both failures share one trigger.
Trigger: the lead agent resumes an existing subagent session by passing opencode's Task tool
task_id. This sends a second prompt into the same child session. T3 then writes aprovider-turn.updatedfor the child's second turn using ordinal 1 again. That collides with the child's first turn onUNIQUE(provider_thread_id, ordinal). TheEventSink.writefailure then fails the parent turn ("Provider turn failed.").ses_ec0a70275ffemMJ0KJEwxTU6UMses_ec09b890bffes49xaBlE4s3vnY2026-10-15T11:36:34Z orchestrationV2.EventSink.write thread_id=thread:provider:opencode:native-thread:ses_ec0a70275ffemMJ0KJEwxTU6UM 2026-10-15T11:49:09Z orchestrationV2.EventSink.write thread_id=thread:provider:opencode:native-thread:ses_ec09b890bffes49xaBlE4s3vnY ProjectionStoreApplyEventError: Failed to apply orchestration projection event provider-turn.updated. UNIQUE constraint failed: orchestration_v2_projection_provider_turns.provider_thread_id, orchestration_v2_projection_provider_turns.ordinalWhat follows:
running. opencode keeps executing the turn. The provider event log shows the resumed subagent still editing files (file.editedat 11:50:03, 11:50:43 and 11:51:37) after T3 reported the failure at 11:49:09. This is unobserved work changing the workspace.ProviderAdapterTurnStartError("The provider could not start this turn").Likely fix: derive child provider-turn ordinals from the existing max for that
provider_thread_id, or key them by native turn/message ID, so that a resumed subagent session appends a turn instead of colliding. Separately, a projection write failure for a child thread should not fail the parent turn while the native turn keeps running.Workaround: don't resume opencode subagents by
task_id; dispatch a fresh subagent instead.lageorgem commentedon Oct 15, 2026
Thanks. Agreed that #19414 covers the budget wedge here. The trigger described in my update above (a resumed opencode subagent reuses provider-turn ordinal 1, hits a UNIQUE violation and fails the parent turn while opencode keeps running) is a separate defect that #19414 does not touch. I filed it as #19883 so this issue can close with #19414.
ashleysommer commentedon Oct 15, 2026
I'm hitting this too. Another datapoint from me:
We hit what looks like the same wedge as this issue, and kept the thread around in its wedged state long enough to measure it. Posting the quantitative details in case they help pin the fix.
Thread profile. Native T3 thread (not imported), single OpenCode native session throughout, same model the whole time (
GLM-5.3-Flash, context window 512,000). 35+ runs over two days; the session legitimately filled up. No subagent or delegate used.The state when the wedge began. The last successful provider turn completed at
usedTokens: 458161 / maxTokens: 512000. After that, every send failed within ~250 ms withContextHandoffBudgetError("Insufficient context allowance…"), and the run was rejected before any provider turn was created.Why the budget is zero — the reserve, not the cap. From
handoffBudget():At 458,161/512,000 the reserve term alone (
max(16000, ceil(512000/4))= 128,000) drives the budget negative — the wedge begins at 75% of the context window, well before the provider is actually out of room. Worth noting in case the reserve is intentional but the failure mode (permanent wedge, see below) is not.Why
/compactcannot recover it./compactdoes compact the provider session (OpenCode records a completed manual compaction with a summary), but the compaction turn completes without reporting token usage — its provider-turn payload hastokenUsage: {}. The handoff budget only readscontextUsagefrom the last provider turn that reported usage, so the stored snapshot stays frozen at the pre-compaction 458k. Every subsequent turn dies atdeliverContextHandoffsbefore a provider turn runs, so nothing ever refreshes the number. Circular deadlock: compaction fixes the real context but cannot fix the snapshot, and the snapshot is all the budget trusts. We confirmed both halves:provider-thread.contextUsageremained{usedTokens: 458161}for hours after compaction (through app restarts and an OpenCode v2.0.29 update), and the newest rejected-dispatch receipts kept logging the same failure.Model switching does not help: switching models reuses the same provider thread and the same cached snapshot.
What recovered the thread. Manually correcting the stored
contextUsagesnapshot (DB-level; set it to a plausible post-compaction value) — the next send then passed the budget check, delivered the handoff, and the first successful provider turn reported fresh usage, after which the thread stayed healthy. So the minimal fix is "get one honest usage report after the compaction", but nothing in the app triggers one while wedged.Suggestions:
contextUsagebefore computing handoff budgets (the stale snapshot is the wedge).Happy to provide the DB snapshots (wedged + recovered) or specific row dumps if useful.
topitopongsala commentedon Oct 16, 2026
Same wedge on the Cursor provider (Grok 4.7 through
@cursor/sdk), on0.0.46-nightly.20261011.2994, Linux x86_64. Cursor hits it through the same two gaps that are described here for OpenCode.Why the budget is 0 on Cursor (traced in the bundled server code and
statev2.sqlite):getModelContextWindow, soknownModelWindowis undefined.tokenUsage, and the Cursor provider thread has nocontextUsage. As a result,handoffUsageis null andhandoffBudgetfalls back towindow = 128000.nativeis thennativeContextEstimate: the byte length of all stored history for the native thread. For one affected thread this was 264,726 bytes, of which 225,761 bytes were command output.max(0, min(16000, 128000 - 264726 - message - 32000)) = 0.The live catalog shows that Cursor's real window for
grok-4.7is 256K or 500K (thecontextWindowoption, default500k), so this thread was far from full. The run'smodelSelectiondoes not carry thecontextWindowoption, and the budget never reads it.Two ways in, both seen on 2026-10-15 (6 Cursor threads, 34 failed turns, one thread with 24):
The provider could not start this turn(ProviderAdapterTurnStartError). Every later turn then needs adelta_since_target_last_seenreplay for the missed run, gets budget 0, and fails in about 100 ms before anyrun.startreaches Cursor. Each failure becomes one more missed run.What recovers it: switching the thread to a Claude instance. Claude reports real
usedTokenswith a 1M window, and the first turn after the switch completed in each case./compactcannot help, becausegetTurnStartHistoryreturns every stored item with no compaction boundary, so the byte estimate does not shrink.Suggested fixes, for Cursor in particular:
getModelContextWindow, read from the selected (or default)contextWindowoption.In our case the first failed turn came while T3 had marked the previous run cancelled but never sent
run.cancelto Cursor. I will post that part on #15447.kodaZD commentedon Oct 20, 2026
Additional OpenCode/T3 v0.1.0 datapoint on Linux.
I hit the permanent
Insufficient context allowance for the provider handoffwedge on a parent OpenCode thread after heavy delegated work.Environment:
opencodesunsilk/gpt-5.6-terraTimeline:
delegate_taskchildren, then a provider-native OpenCode QC child.provider_turnremainedrunningwith nocompletedAt.continuefailed before a new provider turn was created with:Insufficient context allowance for the provider handoff.../compactthen succeeded. The server trace records a successfulopencode.session.summarizetaking 62.8 seconds, and T3 recordsContext compacted.continuestill failed at the same handoff budget gate, before reaching OpenCode.Read-only projection observations:
contextUsage: null.manual_contextsubagent-result handoffs.deliverContextHandoffs, not an upstream model failure.The project work itself was recovered into a fresh thread. I did not mutate
statev2.sqlite.This appears covered by PR #19414's defer-instead-of-throw behavior. One additional concern is that successful
/compactdid not clear or refresh the state that made later handoff-budget calculations reject the thread.