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
{{ message }}
Repository navigation
core: failed resumed turn leaves suspended claim until the next restart #56314
When restart continuity resumes a suspended Session, a transient provider/model failure inside the resumed turn can leave the Session suspended with its claim and pending background job row intact. The parent then remains silent indefinitely; the pending work is retried only after a later server restart.
This is distinct from the durable parent/child watcher problem in #36349 and from the plugin-prompt continuation report in #50751. The failure occurs after the host has already resumed the Session and the resumed turn itself fails.
Steps to reproduce
Start OpenCode in managed service mode with persistent storage.
Start a parent Session and an in-flight background child.
Terminate the server while the parent/child are suspended.
Before the next startup, configure the provider used by the resumed turn to fail deterministically (for example, point its base URL at a closed local port).
Start the service and wait for restart continuity to resume the Session.
Restore the provider without restarting OpenCode.
Observe that the resumed turn has failed, the Session remains suspended, the pending background job remains, and the parent receives no completion.
Restart the service again; the next sweep retries the work and it can complete.
Expected behavior
A failed resumed turn should release or re-queue its restart claim and expose a retryable terminal/recovery state, so a transient provider failure does not strand the Session until an unrelated future restart.
Actual behavior
The resumed turn dies after a partial assistant message; the suspension marker/claim and pending background job remain. No retry or user-visible recovery occurs during the running service.
Evidence
This was reproduced in an isolated OpenCode v2.0.30 managed-service instance while investigating oh-my-opencode-slim issue #1560. On the first restart, the resume attempt counter advanced to 1 and the pending state remained; a second restart advanced the counter and allowed recovery after the provider was restored.
The restart continuity code documents a retry budget for a crash inside the resumed turn, but the current behavior appears to reconcile it only on a subsequent sweep.
Thanks for the detailed report. I couldn't reproduce this on 2.0.30.
What I tried:
Ran the managed background service with persistent storage and a mock provider that never answers, so a turn stays in flight.
Killed the service with kill -9 while the Session was claimed.
Pointed the provider's baseURL at a closed local port and ran opencode service start.
Restart continuity resumed the Session (resume counter went to 1). While the resumed turn was retrying the provider, the Session stayed claimed, as expected for an active turn. After the capped retry schedule (about 84s of backoff) the turn failed with a provider.transport "ConnectionRefused" error. The claim was then released and the resume counter reset, with no second restart needed.
I also tested on the latest dev (4a53028) a suspended parent with a running background subagent whose resumed turn fails with a transport error. The child's claim was released, the pending background job was cleared, and the parent got the subagent result with state="error". That test uses a faked runner, so it isn't a full end-to-end run.
These would help us find the difference:
How long after the restart you checked. During the retry window (about 1.5 minutes) the Session is still legitimately claimed.
Whether the stranded Session was the parent or the background child, and how the child was started (built-in subagent tool or a plugin such as oh-my-opencode-slim).
The server log from the second start, around the resumed turn, and the last messages of the stranded Session (e.g. whether it ends with an idle entry with outcome failed).
Related issues: #56313 (non-service startup skips restart continuity), #36349 (subagent parent completion across restart), #48657 (claim never released when snapshot capture stalls).
Retested properly and I have to retract this — you were right.
Same harness, but this time I did not restart again and sampled for 4.8 minutes:
T+
time_suspended
resume_attempts
idle_outcome
kv job.background/*
parent synthetic
48s
both still suspended
1
—
1
restart notice only
108s
cleared
0
failed
0
source:"subagent", state:"error"
288s
cleared
0
failed
0
unchanged
So the claim is released and the counter resets after the capped retry schedule, the pending background job is cleared, and the parent does receive state="error" — no second restart needed, exactly as you described.
My earlier evidence was taken at T+40s, inside the ~84s backoff window, which is indistinguishable from a stranded session; I then restarted before the window closed, so I never saw the release. The only other contributing factor: nothing about the retry is visible at the default log level (no transport/refused line at INFO).
Closing as not planned — it was my report. For future triage, the discriminator I should have used: time_suspended + resume_attempts >= 1 + a job.background/ row is the normal retry window; it ends with idle/outcome:"failed" and a state="error" completion for the parent. Thanks for the precise reproduction notes — they're what let me find my own measurement error.
Thanks for retesting and posting the timeline. That matches what I saw: during the capped retry window (about 84s of backoff), the Session is still active and keeps its claim. Once the window ends, the turn finishes with an idle entry whose outcome is failed. The claim is released, the resume counter resets, and the parent gets the subagent result with state="error". Nothing else is needed for this report.
You also noted that the retries don't appear in the log at the default INFO level, which makes a normal retry window look like a stuck Session. If you'd like that changed, please open a separate issue for it.
I'm closing this as not planned, since you retested and confirmed it isn't a bug. A failed resumed turn releases its claim once the capped retry window ends (about 84s of backoff). The pending background job is then cleared and the parent gets the state="error" result, with no second restart needed. Thanks again for following up with the timeline. If the retries not showing in the log at the default level is worth changing, please open a separate issue for it.
Activity
opencode-agent commentedon Oct 17, 2026
Thanks for the detailed report. I couldn't reproduce this on 2.0.30.
What I tried:
kill -9while the Session was claimed.baseURLat a closed local port and ranopencode service start.Restart continuity resumed the Session (resume counter went to 1). While the resumed turn was retrying the provider, the Session stayed claimed, as expected for an active turn. After the capped retry schedule (about 84s of backoff) the turn failed with a
provider.transport"ConnectionRefused" error. The claim was then released and the resume counter reset, with no second restart needed.I also tested on the latest
dev(4a53028) a suspended parent with a running background subagent whose resumed turn fails with a transport error. The child's claim was released, the pending background job was cleared, and the parent got the subagent result withstate="error". That test uses a faked runner, so it isn't a full end-to-end run.These would help us find the difference:
idleentry with outcomefailed).Related issues: #56313 (non-service startup skips restart continuity), #36349 (subagent parent completion across restart), #48657 (claim never released when snapshot capture stalls).
HeZ2z commentedon Oct 17, 2026
Retested properly and I have to retract this — you were right.
Same harness, but this time I did not restart again and sampled for 4.8 minutes:
So the claim is released and the counter resets after the capped retry schedule, the pending background job is cleared, and the parent does receive state="error" — no second restart needed, exactly as you described.
My earlier evidence was taken at T+40s, inside the ~84s backoff window, which is indistinguishable from a stranded session; I then restarted before the window closed, so I never saw the release. The only other contributing factor: nothing about the retry is visible at the default log level (no transport/refused line at INFO).
Closing as not planned — it was my report. For future triage, the discriminator I should have used: time_suspended + resume_attempts >= 1 + a job.background/ row is the normal retry window; it ends with idle/outcome:"failed" and a state="error" completion for the parent. Thanks for the precise reproduction notes — they're what let me find my own measurement error.
opencode-agent commentedon Oct 17, 2026
Thanks for retesting and posting the timeline. That matches what I saw: during the capped retry window (about 84s of backoff), the Session is still active and keeps its claim. Once the window ends, the turn finishes with an idle entry whose outcome is
failed. The claim is released, the resume counter resets, and the parent gets the subagent result withstate="error". Nothing else is needed for this report.You also noted that the retries don't appear in the log at the default INFO level, which makes a normal retry window look like a stuck Session. If you'd like that changed, please open a separate issue for it.
opencode-agent commentedon Oct 20, 2026
I'm closing this as not planned, since you retested and confirmed it isn't a bug. A failed resumed turn releases its claim once the capped retry window ends (about 84s of backoff). The pending background job is then cleared and the parent gets the
state="error"result, with no second restart needed. Thanks again for following up with the timeline. If the retries not showing in the log at the default level is worth changing, please open a separate issue for it.