Repository navigation
Description
Summary
A pending TUI form remains visible after a managed-service restart, but submitting it fails because the replacement server no longer has the form in its process-local cache. This leaves the user looking at an actionable form that can never be answered.
@kitlangton encountered this during an automatic next-channel upgrade.
Environment
- opencode version: Form created on
0.0.0-next-15386; failure observed after automatic upgrade to0.0.0-next-15388 - OS: macOS 25.5.0, Darwin 25.5.0 arm64
- Terminal: WarpTerminal,
TERM=xterm-256color,COLORTERM=truecolor - Shell:
/bin/zsh - Install/channel: global
opencode2,nextchannel, managed background service - Active plugins:
~/.config/opencode/plugins/share-with-team.ts,packages/opencode/src/control-plane/dev/debug-workspace-plugin.ts,@warp-dot-dev/opencode-warp
Reproduction
- Open a TUI session and let the agent create a question/form.
- Leave the form pending.
- Restart or automatically upgrade the managed service while the form is visible.
- Reconnect to the session and submit an answer.
Expected Behavior
The pending form should remain answerable after reconnect, or the TUI should recognize that it is no longer live and dismiss or replace it with a recoverable state.
Actual Behavior
The TUI continues rendering the form and accepts local selection/editing, but submission fails with:
Form not found: frm_f59082e560014GRjWpWb32sAxO
The API confirms the stale state:
{"_tag":"FormNotFoundError","id":"frm_f59082e560014GRjWpWb32sAxO","message":"Form not found: frm_f59082e560014GRjWpWb32sAxO"}Additional Context
The restart timeline is deterministic in the local logs:
2026-07-13T01:14:18.597Z updated OpenCode from=0.0.0-next-15386 to=0.0.0-next-15388
2026-07-13T01:14:41.678Z old managed server begins shutting down
2026-07-13T01:14:43.281Z replacement 0.0.0-next-15388 service starts
The service remained healthy after replacement:
{"healthy":true,"version":"0.0.0-next-15388","pid":25498}The likely boundary is packages/core/src/form.ts: forms and their pending state are stored in a location-scoped in-memory Cache. The TUI retains the rendered form across reconnect, but the replacement location graph has no matching cache entry. There is no form persistence table in the next-channel database, and querying the exact form ID on the replacement server returns FormNotFoundError.
Session messages otherwise continued after the service replacement, so this is specifically stale/lost form state rather than a generally dead session.
Activity
kitlangton commentedon Jul 13, 2026
The immediate stale-form escape hatch is in draft PR #36591. The durable long-term solution is tracked in #36347, with the reviewed plan at
specs/v2/durable-session-interactions.md.That plan deliberately starts with durable Session questions and does not claim form persistence alone restores the interrupted Tool continuation.
sahiljagtap08 commentedon Aug 23, 2026
Opened #44342 as the client side escape hatch: bootstrap now hydrates pending prompts from the live server, and a reply that fails for a request the server no longer knows dismisses the form instead of leaving it stuck. The durable fix stays with #36347.
fix: clear stale forms on server reconnect
neriousy commentedon Oct 22, 2026
Fixed in #44516: replying to a form the restarted server no longer knows now dismisses it instead of leaving it stuck. Keeping forms answerable across restarts is tracked in #36347.
Saka-CS commentedon Oct 27, 2026
Data point on v2.1.2 (Linux, Hyprland 0.56.2, Ghostty 1.3.1): a pending form no longer becomes unanswerable in the trapping sense after a service restart.
Setup: isolated server (
opencode serve --port 4599,OPENCODE_PASSWORDset), TUI attached with--server, question form created viaPOST /api/session/{id}/form, then the server process was killed and restarted on the same port.Result:
FormNotFoundErrorfrom the API), i.e. pending forms are still process-local.FormNotFoundError/ unhandled-rejection lines for the reconnect window.So on 2.1.2 the loss of the pending form is recovered client-side; only the answer is lost. If this is still seen on 2.1.x, the trigger is probably different from a plain restart.