Repository navigation
Description
Description
OpenCode can continue into a child-spawning tool call after a user interrupt has already been delivered, then fail to return control within the bounded run.
The standalone reproducer installs public [email protected], starts a local mock provider, schedules SIGINT 800 ms after the CLI starts, and delays the provider tool call for 2000 ms. The reproduction succeeds only when the delayed child-spawning tool call still records its PID side effect and the process exits by timeout/signal.
Plugins
None.
OpenCode version
1.17.9
Steps to reproduce
Prerequisites: Docker, Python 3, and the GitHub CLI (gh) for the clone command below. The linked reproducer is self-contained and uses only Python standard-library modules plus Docker. It installs the affected OpenCode version from the public npm package, starts a local mock provider, and runs the CLI in an isolated workspace. The Docker run is limited to 2 CPUs and 4 GiB RAM by default.
Complete self-contained reproducer: https://gist.github.com/N0zoM1z0/b6b7daaf046c5d51345a54f886b11b61
The Gist contains opencode-interrupt-child-late-effect.reproduce.py. Download and run:
gh gist clone b6b7daaf046c5d51345a54f886b11b61 opencode-interrupt-child-late-effect-reproducer
cd opencode-interrupt-child-late-effect-reproducer
python3 opencode-interrupt-child-late-effect.reproduce.pyTo reuse an already-built local image:
python3 opencode-interrupt-child-late-effect.reproduce.py --skip-buildExpected successful reproduction output includes:
provider_requests_at_least: observed=... expected>=1
side_effect_file_exists interrupt-child.pid: bytes=... expected>=1
timeout_or_signal: returncode=137 timed_out=True
REPRODUCED
Actual behavior: OpenCode receives the user interrupt before the delayed provider tool call is sent, but the child-spawning tool still records its side effect and the CLI only exits by timeout/signal.
Expected behavior: A user interrupt should prevent later provider-stream tool calls from causing local side effects, and the CLI should return control boundedly.
Screenshot and/or share link
Not applicable. The linked reproducer prints the provider transcript checks and REPRODUCED.
Operating System
Linux x86_64 Docker, using a node:24-bookworm-based target image.
Terminal
Non-interactive CLI run from Python subprocess.
Activity
JihoonJeong commentedon Jul 21, 2026
I can corroborate this at the provider-transport boundary on OpenCode 1.18.3 and 1.18.4.
Using an isolated
opencode serveplus a loopback OpenAI-compatible SSE provider, I start an actual configured-model turn, wait until the provider emits a valid partial chat-completion SSE frame, then callPOST /api/session/:id/interrupt.Observed on both versions:
/api/session/activeremoves the sessionabortednor responseclosewithin 5 secondsSo the application state is cleaned up, but the in-flight provider HTTP/SSE stream remains alive. That leaves a path for a late provider tool call like the one in this issue to arrive after cancellation.
The relevant cancellation files are unchanged from v1.18.3 through v1.18.4 and current
devat4cc022481c184cb9d5e14f7e882b6749d2414553:packages/core/src/session/run-coordinator.tspackages/core/src/session/runner/llm.tspackages/llm/src/route/transport/http.tsExpected invariant: once interrupt completes, the provider response body/socket is cancelled promptly and no later provider event can be admitted or execute a tool. I can provide the self-contained TypeScript probe and secret-free JSON reports if useful.
github-actions commentedon Sep 19, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
N0zoM1z0 commentedon Sep 19, 2026
Hi, adding a current-version recheck since this was auto-closed today.
I revalidated the original standalone Docker reproducer against the current npm latest,
[email protected](checked 2026-09-19 UTC). The only change to the repro case was updating the package/image version from1.17.9to1.18.31.Result: this still reproduces consistently for me. I ran one build run and then three
--skip-buildreruns against the same1.18.31image; all reproduced. The reruns all ended with:process_exit=137process_timed_out=Trueprovider_requests=2to/v1/messagesside_effect_file_exists interrupt-child.pidtimeout_or_signal: returncode=137 timed_out=TrueSo the child-spawning tool side effect is still observed after the scheduled interrupt, and the CLI still only returns via the reproducer timeout/signal.
I also did not find a linked merged PR on this issue's timeline that directly covers #33364. Related issue #40829 still references this as the separate “tool starting after cancellation” case.
Could we reopen this as still current? If you would prefer a fresh issue because of the bot auto-close message, I can also file a new one with the
1.18.31retest details.