Repository navigation
Description
Summary
When the TUI's connection to the shared background service drops while an agent-initiated interactive question form is pending, the server discards the pending form and does not restore it on reconnect. The TUI keeps its stale modal mounted; every subsequent keypress produces a 404 FormNotFoundError unhandled rejection, keyboard input stays trapped in the dead modal, and the terminal must be killed to recover.
Environment
- opencode version: opencode2 v0.0.0-beta-17963 (channel: beta)
- OS: Darwin 25.5.0 arm64
- Terminal: WarpTerminal
- Shell: /bin/zsh
- Install/channel: beta, shared background service (
opencode2 service statusreported http://127.0.0.1:) - Active plugins: several local plugins are configured, but all failed schema validation at load time in this session ("Expected object at ["default"]"), so the failure occurred with no custom plugins active.
Reproduction
- Start the TUI against the shared background service.
- Trigger a tool call that opens an interactive question form and leave it pending.
- Drop the client↔server connection while the form is open. In my case the drop happened on its own ~10 minutes into the wait; server logged
location services evictedfor the project directory. - Client silently reconnects (~45 s later; server re-runs location services boot and plugin load).
- Press any key in the still-displayed form (Enter or Esc).
Observed once so far; likely reproducible whenever the connection drops while a form is pending.
Expected Behavior
Either the server persists and restores pending forms across client reconnects, or the client detects the stale form (404 / FormNotFoundError) and dismisses the modal, returning keyboard control to the user.
Actual Behavior
-
On reconnect the TUI replayed its pending answer:
POST /api/session/:id/form/:formId/reply→ 404. -
Every later keypress produced another 404 on
/replyor/cancelplus an unhandled rejection in the CLI log:level=ERROR role=cli message="unhandled rejection" cause._tag=FormNotFoundError cause.message="Form not found: frm_…" -
77 such rejections over ~100 seconds.
-
The modal remained mounted and captured all keyboard input; Esc/Ctrl+C did not work; only killing the terminal recovered.
-
The assistant message was left incomplete with the tool stuck in
running; the session ended asinterrupted.
Additional Context
- Server side of the same window: repeated
Sent HTTP response … http.url=/api/session/<id>/form/<formId>/reply http.status=404and matching/cancel404s after the reconnect. - No pending permission remains queryable afterwards (
GET /api/session/:id/permission→ empty). - Workaround: kill the terminal, start a new session. The run's completed work is intact; only the unanswered question is lost.
- Session/form IDs redacted. Happy to provide a fuller sanitized log excerpt.
Activity
neriousy commentedon Oct 22, 2026
The unexitable modal is fixed in #44516: when the server no longer knows a form, the client now drops it instead of looping on FormNotFoundError. Keeping pending forms across a reconnect or eviction is tracked in #52599 (PRs #51583 and #52600), so closing this in favor of that.
Saka-CS commentedon Oct 27, 2026
Attempted to reproduce this on v2.1.2 and it no longer reproduces on this setup.
Environment
TERM=xterm-ghostty) on Hyprland 0.56.2, Linux x86_64opencode serve --port 4599withOPENCODE_PASSWORDset so the password survives a restart), TUI attached with--server http://127.0.0.1:4599 --session <id>— the shared background service was not touchedPOST /api/session/{id}/formand state checked withGET /api/session/{id}/form/{formId}Steps
Result
GET /api/session/{id}/form/{formId}returnsFormNotFoundErroras expected.FormNotFoundErrorand 0 unhandled rejections in the client log after the reconnect (I counted new log lines since the restart).I also tested the related trigger of two clients on the same session: question form visible in both TUIs, answered in client A. Client B's dialog disappeared cleanly within ~3 s, no 404s, no trapped keyboard.
Caveat: forms were created through the API rather than by the model calling the
questiontool, and a server restart is only a proxy for the original location-eviction trigger. But the client-side stale-modal handling does look fixed on 2.1.2.