Repository navigation
[Bug]: Resuming an OpenCode subagent (Task task_id) reuses provider-turn ordinal → UNIQUE violation fails parent turn while opencode keeps running #19883
Copy link
Copy link
Open
Labels
acceptedfeature request acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
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, have the lead agent dispatch a subagent with opencode's Task tool and let it complete. T3 projects the child's turn as provider turn ordinal 1 on the child provider thread.
- In a later step of the same parent turn, have the lead resume that subagent by passing its
task_id. This sends a second prompt into the same child native session. - Wait for the resumed child turn to finish.
Expected behavior
The child's second turn is recorded as ordinal 2 on the same provider thread, and the parent turn continues.
Actual behavior
- The second child turn reuses ordinal 1. The
provider-turn.updatedprojection write for it fails withUNIQUE constraint failed: orchestration_v2_projection_provider_turns.provider_thread_id, ordinal. - T3 fails the parent turn with "Provider turn failed." The parent provider turn is left
runningwith no completion or token usage. - opencode does not stop. The parent session keeps running unobserved: it consumed the child's result, dispatched more subagents and made 6 commits, about 15 minutes of work that never appears in the T3 timeline.
- Every new message is rejected. While that hidden turn runs, each message fails with
ProviderAdapterTurnStartError("The provider could not start this turn") because the native session is busy. To the user the thread looks dead. Aborting the native session through the opencode HTTP API (POST /session/:id/abort) was the only way to recover it. - It can lead to [Bug]: OpenCode thread permanently wedged by "Insufficient context allowance" after a failed turn; /compact cannot recover it #19875. Those rejected runs become missed runs. On a long thread the replay handoff then hits
ContextHandoffBudgetErrorand wedges the thread ([Bug]: OpenCode thread permanently wedged by "Insufficient context allowance" after a failed turn; /compact cannot recover it #19875, PR fix(server): a handoff too large for the context window no longer wedges the thread #19414). PR fix(server): a handoff too large for the context window no longer wedges the thread #19414 fixes that wedge but not this trigger.
This reproduced twice: on the original thread, and on a fresh fork of it within its first turn.
| Parent thread | Child native session | Child's existing turn | Collision |
|---|---|---|---|
| 4c5716b5…, run 14 | ses_ec0a70275ffemMJ0KJEwxTU6UM |
ordinal 1, completed 11:35:59–11:36:13Z | 11:36:34Z |
| f4bc3d47… (fork), run 1 | ses_ec09b890bffes49xaBlE4s3vnY |
ordinal 1, completed 11:48:31–11:48:55Z | 11:49:09Z |
Suggested fixes:
- Allocate ordinals by appending. Allocate child provider-turn ordinals as
max(ordinal) + 1for theprovider_thread_id, or key them by native turn/message ID, so a resumed subagent session appends a turn. - Don't fail the parent while its native turn is still running. A projection write failure for a child thread should not fail the parent turn while that turn keeps running. If T3 does fail the run, it should abort the native session too, so that T3 and opencode agree on the state (compare Transient "database is locked" during an event write fails the run but leaves the Claude process running unsupervised #15542 and [Bug]: Cursor run continues after T3 marks parent and native subagents failed #15447).
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, anthropic/claude-opus-5-5 via opencode, full-access runtime
Logs or stack traces
2026-10-15T11:49:09Z orchestrationV2.EventSink.write
orchestration_v2.thread_id=thread:provider:opencode:native-thread:ses_ec09b890bffes49xaBlE4s3vnY
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
# (identical at 11:36:34Z for ses_ec0a70275ffemMJ0KJEwxTU6UM)
# Parent thread f4bc3d47 after the collision:
run 1: failed "Provider turn failed."; parent provider turn status=running, completedAt=null
run 2 (11:50:56Z), run 3 (11:55:50Z): failed in ~200 ms, ProviderAdapterTurnStartError
opencode GET /session/status at ~12:00Z:
ses_ec09bf7a3ffejuKpInFs6JfJxi (parent) busy
ses_ec094e0bc… / ses_ec094a727… (new subagents) busy
worktree gained 6 commits 14:53-15:04 local while T3 showed the thread as failedWorkaround
- Don't resume opencode subagents by
task_id. Dispatch a fresh subagent instead. - If it already happened, abort the native session. Call
POST /session/<parent ses_id>/aborton the T3-managed opencode server; the port is in~/.t3/userdata/opencode-servers/*.json. Then send a new message.
Related: #19875 (the downstream budget wedge, fixed by #19414), #15542, #15447.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
acceptedfeature request acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Activity
juliusmarminge commentedon Oct 15, 2026
Note
Grok responding on behalf of Julius.
This is a confirmed bug on main in the OpenCode adapter's subagent tracking: nothing in that code handles a resumed child session.
In
packages/provider-opencode/src/server/adapter.ts, subagent context is keyed by the Task tool part's id (subagentsByNativeItemId). When a later Task call targets an existing child session, the adapter builds a new context for the same child session and callsthreads.set(childSessionId, …)with a fresh state wherenextChildTurnOrdinal: 1. The child provider-thread id comes from the native session id, so it is the same thread as before. The resumed child turn then getsordinal: state.nextChildTurnOrdinal++, which is 1 again, and hits the unique indexorchestration_v2_projection_provider_turns(provider_thread_id, ordinal)from migration055_OrchestrationV2.ts.This isn't a duplicate. #15542 (Claude, transient DB lock) and #15447 (Cursor) cover the same downstream gap: a failed run leaves the native process running unsupervised. This issue is a separate trigger specific to OpenCode, and the orphaned-run part belongs with those issues.
Workaround for now: have the lead dispatch a fresh subagent instead of resuming one by
task_id.linkendr commentedon Oct 17, 2026
Another reproduction, on the stable release 0.1.0 (desktop + headless service, macOS arm64, opencode 1.18.30,
openai/gpt-6-astravia opencode). PR #19888 is not in 0.1.0: the bundledadapter.tsstill builds the child state with an unconditionalnextChildTurnOrdinal: 1.ses_eb6899e2dffekU65BFTJvXsFvJruns its first Task turnnative-turn:msg_1497661d5001…)task_idUNIQUE constraint failed: orchestration_v2_projection_provider_turns.provider_thread_id, orchestration_v2_projection_provider_turns.ordinalonthread:provider:opencode:native-thread:ses_eb6899e2…runningProviderAdapterTurnStartError; no request reaches opencode, consistent with thealready has an active turnguard instartTurnRecovery was the same as in the report:
POST /session/:id/aborton the opencode server.Two notes on #19888:
max(ordinal) + 1for the provider thread would cover that case too.activeTurnappears to stay set, so every new message is refused until the native session is aborted by hand.