Skip to content

fix: detect interrupted commands in terminal exit code hack - #307256

Merged
Dmitriy Vasyura (dmitrivMS) merged 6 commits into
microsoft:mainfrom
yogeshwaran-c:fix/terminal-interrupted-command-exit-code
Jul 30, 2026
Merged

fix: detect interrupted commands in terminal exit code hack#307256
Dmitriy Vasyura (dmitrivMS) merged 6 commits into
microsoft:mainfrom
yogeshwaran-c:fix/terminal-interrupted-command-exit-code

Conversation

@yogeshwaran-c

@yogeshwaran-c Yogeshwaran C (yogeshwaran-c) commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix

What is the current behavior?

When a command is typed and interrupted with Ctrl+C (for example, echo test^C), the TerminalShellExecution end event can report an exit code inherited from the previous execution of the same command.

For example:

$ echo test     -> exit code 0 (correct)
$ echo test^C   -> exit code 0 (incorrect; should be undefined)

This happens because handleCommandFinished contains a workaround for versions of bash whose history output merges identical commands. When bash reports an undefined exit code and the shell integration reports the same command text as the previous command, the workaround reuses the previous exit code. An interrupted duplicate command therefore looks like the history-merge case and incorrectly inherits the previous result.

Closes #237517

What is the new behavior?

CommandDetectionCapability now listens to the existing PromptInputModel.onDidInterrupt event, which detects the Ctrl+C/ETX input directly. When the current command was interrupted, the duplicate-history workaround is skipped and the undefined exit code reported by the shell is preserved. This matches the documented TerminalShellExecutionEndEvent.exitCode contract for commands canceled via Ctrl+C.

The interrupted state is reset when the next prompt starts. Non-interrupted duplicate commands continue to use the existing bash history workaround unchanged.

Using the prompt input model's interrupt signal also avoids inferring cancellation from rendered ^C text, so a literal command ending in ^C is not treated as interrupted.

Additional context

The regression tests verify that:

  1. An interrupted duplicate command keeps an undefined exit code instead of inheriting the previous command's exit code.
  2. A non-interrupted duplicate command still inherits the previous exit code, including when its literal text ends in ^C.

The tests also retain the initial command in their expected command lists and no longer use unnecessary any casts.

Validation:

  • commandDetectionCapability.test.ts: 10 passing
  • npm run precommit: passed

When a command is interrupted with Ctrl+C, the shell integration's
history-based command detection incorrectly inherits the previous
command's exit code. This happens because bash's `history` deduplicates
identical commands, so the hack that compensates for this sees the same
command text with an undefined exit code and assigns the last known one.

Fix by checking if the raw command line in the terminal buffer ends
with ^C before applying the history-merge heuristic. When interrupted,
report exit code 130 (128 + SIGINT) instead of inheriting.

Closes microsoft#237517

@meganrogge Megan Rogge (meganrogge) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@alexr00 Alex Ross (alexr00) removed this from the 1.116.0 milestone Apr 13, 2026
Copilot AI review requested due to automatic review settings July 29, 2026 00:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes terminal shell execution exit codes for commands interrupted with Ctrl+C.

Changes:

  • Detects raw command lines ending in ^C and reports exit code 130.
  • Adds regression tests for interrupted and duplicate commands.
Show a summary per file
File Description
commandDetectionCapability.ts Adds interrupted-command detection.
commandDetectionCapability.test.ts Adds regression tests; both assertions currently omit the initial command and fail.

Review details

Comments suppressed due to low confidence (2)

src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts:176

  • The first printStandardCommand is still present when this assertion runs, so assertCommands compares two actual commands against one expected command and the test fails. Keep the prior command in the expected list since it is required for the inheritance scenario.
		assertCommands([
			{ command: 'echo test', exitCode: 0, cwd: undefined, marker: { line: 2 } }
		]);

src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts:170

  • The method parameter is number | undefined, making this any cast unnecessary and contrary to the repository's no-any guideline.
		capability.handleCommandFinished(undefined as any);
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Medium

Use the prompt input model's Ctrl+C signal to avoid applying the duplicate-history exit code workaround to canceled commands. Update the regression tests to cover the real duplicate-history command line and retain prior commands in expectations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dmitrivMS

Copy link
Copy Markdown
Contributor

Updated the fix in cfa3acd:

  • Use the existing PromptInputModel.onDidInterrupt signal instead of inferring Ctrl+C from rendered ^C text.
  • Preserve an undefined exit code for canceled commands, matching the TerminalShellExecutionEndEvent.exitCode API contract, rather than synthesizing exit code 130.
  • Keep the duplicate bash history workaround for non-interrupted commands.
  • Update the regression tests to model the duplicate-history command line, include the initial command in expectations, remove the unnecessary any casts, and verify literal ^C text is not treated as an interruption.

Validation:

  • commandDetectionCapability.test.ts: 10 passing
  • npm run precommit: passed

@dmitrivMS
Dmitriy Vasyura (dmitrivMS) enabled auto-merge (squash) July 29, 2026 06:15
@dmitrivMS
Dmitriy Vasyura (dmitrivMS) merged commit 3670eec into microsoft:main Jul 30, 2026
46 of 47 checks passed
@vs-code-engineering vs-code-engineering Bot added this to the 1.132.0 milestone Jul 30, 2026
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.

Incorrect TerminalShellExecution exit code

6 participants