`ai-opencode`: `RUN_FINISHED` never arrives when an adapter teardown step does not settle
Maintainers usually reply within 1 day
@tombeckenham is already working on this.
Since Oct 6, 2026.
Assessment
This issue has not been assessed yet.
Description
chat() holds RUN_FINISHED until the adapter's chatStream generator returns. In @tanstack/ai-opencode 0.4.14, the finally block of OpencodeTextAdapter.chatStream awaits handle.dispose(), server.dispose(), and bridge.close() in sequence. If one of them does not settle, the text streams but the run never ends, and the HTTP response stays open.
Repro
- Run
chat()withopencodeText(...),withSandbox(...), and at least one tool, so that a tool bridge opens. - Wrap the sandbox provider so that
process.kill()on a spawned process returns a promise that never settles (as a sandbox API call that never returns does). Or use anopencodeonPATHthat ignoresSIGTERMand keeps a request open on the tool bridge. - Observe:
TEXT_MESSAGE_ENDarrives, butRUN_FINISHEDdoes not.
Proposed fix
Give each teardown step a time limit, and log a step that fails:
const bounded = (step) => Promise.race([
Promise.resolve().then(step).catch((error) => logger.errors("opencode teardown failed", { error, source: "opencode.chatStream" })),
new Promise((resolve) => setTimeout(resolve, 1000).unref()),
])
await bounded(() => handle?.dispose())
await bounded(() => server?.dispose())
await bounded(() => bridge?.close())
Suggestion (separate, @tanstack/ai-sandbox-local-process)
spawn uses detached: true, so a spawned process group gets no signal when its owner process dies, and the server continues to run with parent 1. One option: start one watchdog per owner with a stdin pipe that only the owner holds, register each spawned group with it, and kill the registered groups when the pipe closes.
- Dominant language
- TypeScript
- Stars
- 3.2k
- Forks
- 361
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 210
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TanStack/ai
-
summarize() sends temperature 0.3 to models that reject sampling parameters (e.g. claude-sonnet-5-5)Possibly taken @jherr claimed this 1 day ago. Openhas-pr waiting-on: maintainer
TanStack/ai#1703 · 1 comment · 1 assignee ·
Maintainers usually reply within 1 day
-
ai-openrouter: malformed Chat Completions tool arguments execute as an empty objectPossibly taken @tombeckenham claimed this 2 days ago. Openhas-pr waiting-on: maintainer
TanStack/ai#1689 · 1 assignee ·
Maintainers usually reply within 1 day
-
StreamProcessor discards metadata from REASONING_MESSAGE_STARTPossibly taken @tombeckenham claimed this 2 days ago. Openwaiting-on: maintainer
TanStack/ai#1667 · 1 assignee ·
Maintainers usually reply within 1 day
-
Async persistence restored before attach() never rejoins an in-flight runPossibly taken @tombeckenham claimed this 5 days ago. Openhas-pr waiting-on: maintainer
TanStack/ai#1639 · 1 assignee ·
Maintainers usually reply within 1 day
-
ai-mcp: support publishing MCP list changes to subscribed clientsPossibly taken @jherr claimed this 5 days ago. Openwaiting-on: maintainer
Difficulty 5/5 Over a week Newbie friendliness 28/100
TanStack/ai#1631 · 1 assignee ·
Maintainers usually reply within 1 day