Skip to content

Fix terminal hang when process exits without shell integration sequences#312827

Merged
meganrogge merged 2 commits intomainfrom
merogge/fix-terminal-hang-on-exit
Apr 27, 2026
Merged

Fix terminal hang when process exits without shell integration sequences#312827
meganrogge merged 2 commits intomainfrom
merogge/fix-terminal-hang-on-exit

Conversation

@meganrogge
Copy link
Copy Markdown
Collaborator

@meganrogge meganrogge commented Apr 27, 2026

Problem

When a terminal process exits without emitting shell integration prompt sequences, trackIdleOnPrompt blocks forever because the Executing state cancels both schedulers and no more data arrives to reschedule them. This causes run_in_terminal to never complete, eventually timing out after 1 hour.

Evidence from eval run 24890545491 (VS Code, terminalbench, gpt-5.4)

Analysis of the VS Code logs (vsc-output/productLogs/terminal.log) found 21 tasks with terminal hangs — commands that started with the rich execute strategy but never completed (no corresponding Finished log line). Of these, 7 timed out entirely (X_AGENT_STILL_RESPONDING), and 14 others lost significant time before recovering or failing for other reasons.

Tasks with terminal hangs that timed out:

Task Rich cmds Finished Hung Shell integration failed
cobol-modernization 13 12 1 yes
crack-7z-hash 42 41 1 yes
csv-to-parquet 1 0 1 yes
make-doom-for-mips 2 1 1 yes
neuron-to-jaxley-conversion 6 5 1 yes
reshard-c4-data 3 2 1 yes
build-initramfs-qemu 3 2 1 yes

Tasks with terminal hangs that partially recovered:

Task Rich cmds Finished Hung Shell integration failed
git-multibranch 19 15 4 yes
make-mips-interpreter 40 35 5 yes
play-zork 63 61 2 yes
polyglot-rust-c 27 24 3 yes
solana-data 21 19 2 yes
build-tcc-qemu 37 34 3 yes
qemu-alpine-ssh 12 10 2 yes
hf-model-inference 5 3 2 yes
blind-maze-explorer-5x5 1 0 1 yes
intrusion-detection 10 9 1 no
kv-store-grpc 4 3 1 no
mlflow-register 9 8 1 yes
run-pdp11-code 20 19 1 no
stable-parallel-kmeans 6 5 1 yes

All show the same pattern: Using 'rich' execute strategy logged with no subsequent Finished line for the hung command.

Also confirmed in eval run 24966302375 (terminalbench2)

A newer eval run showed all 19 out of 19 timeouts caused by the same terminal hang pattern. 15 of those 19 tasks passed in the CLI (run 22941092844) with the same model. See #312853 for details.

Fix

Add onExit to the Promise.race in both richExecuteStrategy and basicExecuteStrategy. When the process exits, the strategy resolves immediately with the available output and exit code instead of hanging indefinitely.

Also:

  • Skip the post-race waitForIdle in basic strategy when the process already exited
  • Fall back to the process exit code when shell integration did not provide one
  • Handle ITerminalLaunchError properly in exit code extraction and logging

Test

Added tests for both strategies:

  • completes when terminal process exits without shell integration sequences — simulates process exit without onCommandFinished, verifying the strategy completes with the correct exit code
  • handles ITerminalLaunchError on process exit — verifies ITerminalLaunchError objects are properly handled

Limitations

This fix handles the process exit case. For long-running commands where the process stays alive but shell integration breaks, onExit won't fire until the process finishes. A complementary fix to downgrade from rich to basic strategy mid-flight is tracked in #312853.

When a terminal process exits (e.g., with exit code 1) without emitting
shell integration prompt sequences, trackIdleOnPrompt blocks forever
because the Executing state cancels both schedulers and no more data
arrives to reschedule them.

Fix by adding onExit to the Promise.race in both rich and basic execute
strategies. When the process exits, the strategy resolves immediately
with the available output and exit code instead of hanging indefinitely.

Also skip the post-race waitForIdle in basic strategy when process has
already exited, since no more data will arrive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 16:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a hang in the terminal-based run_in_terminal execution flow when a process exits without emitting expected shell-integration “prompt” sequences, by allowing the execute strategies to complete based on the terminal process exit event.

Changes:

  • Add onExit to the completion Promise.race in both rich/basic execute strategies to avoid waiting forever on prompt-idle detection.
  • Skip post-race waitForIdle in the basic strategy when the terminal process has already exited.
  • Add a regression test for the rich strategy covering “process exits without shell integration sequences”.
Show a summary per file
File Description
src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/richExecuteStrategy.ts Completes execution on terminal process exit and falls back to the exit event for exit code.
src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/basicExecuteStrategy.ts Completes execution on terminal process exit, skips idle wait on exit, and falls back to the exit event for exit code.
src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/browser/richExecuteStrategy.test.ts Adds a test ensuring rich strategy completes when the terminal process exits without shell integration events.

Copilot's findings

Comments suppressed due to low confidence (2)

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/richExecuteStrategy.ts:161

  • When falling back to onExit for the exit code, exitCodeOrError may be an ITerminalLaunchError that includes a numeric code. Currently this path only handles numbers, so you can lose a real exit code. Consider using the error’s code (when present) and including the message in additionalInformation for easier diagnosis.
			// Determine exit code from shell integration or from the process exit event
			let exitCode = finishedCommand?.exitCode;
			if (exitCode === undefined && onDoneResult && onDoneResult.type === 'processExit') {
				exitCode = isNumber(onDoneResult.exitCodeOrError) ? onDoneResult.exitCodeOrError : undefined;
			}

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/basicExecuteStrategy.ts:212

  • When falling back to onExit for the exit code, exitCodeOrError may be an ITerminalLaunchError with a numeric code. The current implementation only accepts numbers and will drop the error’s code. Consider using the error’s code (when present) and surfacing the message in additionalInformation.
			// Determine exit code from shell integration or from the process exit event
			let exitCode = finishedCommand?.exitCode;
			if (exitCode === undefined && onDoneResult && onDoneResult.type === 'processExit') {
				exitCode = isNumber(onDoneResult.exitCodeOrError) ? onDoneResult.exitCodeOrError : undefined;
			}
  • Files reviewed: 3/3 changed files
  • Comments generated: 3

@meganrogge meganrogge self-assigned this Apr 27, 2026
@meganrogge meganrogge added this to the 1.119.0 milestone Apr 27, 2026
… test

- Add isTerminalLaunchError/formatExitCodeOrError/extractExitCode helpers
  to both rich and basic execute strategies to properly handle onExit
  emitting ITerminalLaunchError objects instead of just numbers
- Add unit test for BasicExecuteStrategy process-exit-without-shell-integration
- Add ITerminalLaunchError test case to RichExecuteStrategy tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@meganrogge meganrogge merged commit d19a99a into main Apr 27, 2026
26 checks passed
@meganrogge meganrogge deleted the merogge/fix-terminal-hang-on-exit branch April 27, 2026 17:56
@meganrogge
Copy link
Copy Markdown
Collaborator Author

cc @anthonykim1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants