microsoft / microsoft/vscode

Shell integration: executeCommand().read() stream and onDidEndTerminalShellExecution both permanently silent (zero data) for certain multi-line / subshell command shapes

Open
#324,392 0 comments 0 reactions 1 assignee Claimed by @anthonykim1 View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

# Title
Shell integration: `executeCommand().read()` stream and `onDidEndTerminalShellExecution` both permanently silent (zero data) for certain multi-line / subshell command shapes

## Does this issue occur when all extensions are disabled?
Not tested directly (reproduced via an extension driving the shell-integration API), but the failure is in VS Code's own OSC-633 parsing/event delivery, not extension logic — see reproduction below, which is a minimal, extension-only repro path.

## Environment
- VS Code Version: 1.100.0 (also reported by users on 1.123–1.127, so not version-specific)
- OS: Linux (Ubuntu 24.04), bash 5.2
- Shell: bash, default profile, real (non-custom) shell integration script

## Summary

When a command executed via `TerminalShellIntegration.executeCommand()` has certain shapes — specifically (a) a command string containing a literal embedded newline, or (b) a `( ... )` subshell — VS Code can permanently stop delivering **any** data on the `execution.read()` stream, and `onDidEndTerminalShellExecution` never fires either. The command itself completes normally and its output is visible in the terminal UI, but the extension-facing API surfaces zero data and no completion signal, indefinitely (observed for 100+ seconds; the underlying shell process has already exited by then).

This is a more severe variant of previously reported issues (#316556, #250764, #254724): those describe the *end marker specifically* being lost while some stream data still arrives; this report is about **all** stream data (including the start marker) being lost.

## Steps to Reproduce

1. Open a VS Code extension host with a workspace open, default bash profile, shell integration enabled.
2. Programmatically create/get an integrated terminal and wait for `terminal.shellIntegration` to become available.
3. Call `terminal.shellIntegration.executeCommand(command)` for either of:
- A command containing a literal newline, wrapped in a `{ }` group to keep it as one execution (needed to avoid VS Code splitting multi-line input into separate executions — see #267344-adjacent behavior):
```
{
python3 -c "
import sys
print('boom', file=sys.stderr)
sys.exit(1)
"
}
```
- Or a `( ... )` subshell, single line:
```
( python3 -c "import sys; print('boom', file=sys.stderr); sys.exit(1)" )
```
4. Call `execution.read()` immediately (before or in the same tick as step 3, per VS Code's own guidance that output before the first `read()` call may be lost) and iterate the returned `AsyncIterable` with `for await`.
5. Also register `vscode.window.onDidEndTerminalShellExecution` before executing.

## Expected Result

Either:
- The stream yields the `]633;C` start marker, the command's output, and the `]633;D` end marker, and the stream closes; **or**
- `onDidEndTerminalShellExecution` fires with the exit code.

## Actual Result

Neither happens. The `for await` loop never receives a single chunk (not even the `]633;C` start marker), and `onDidEndTerminalShellExecution` never fires. Confirmed via 4/4 consecutive runs of an automated e2e test (using `@vscode/test-electron` against this exact VS Code version) that the stream is silent for 100+ seconds while the underlying `python3` process has already exited (verified via a separate `ps` check finding no matching process).

Only `vscode.window.onDidStartTerminalShellExecution` fires — and critically, for the `( )` subshell reproduction, **its own reported `command` field is corrupted**: the script's stderr output ("boom") appears concatenated directly into the value VS Code believes is the command line:

```js
{
command: '( python3 -c "import sys; print(\'boom\', file=sys.stderr); sys.exit(1)" )\nboom',
terminalId: 1
}
```

This strongly suggests the internal OSC-633 sequence parser is misparsing command/output boundaries for these constructs, which likely also explains why no further data is delivered afterward (the parser state machine may be stuck expecting more command-line input rather than transitioning to output-capture mode).

## Additional data point (single-line brace group works fine)

To isolate whether grouping syntax itself was the trigger, we tested a single-line `{ }` group (no embedded newline): `{ python3 -c "import sys; print('boom', file=sys.stderr); sys.exit(1)"; }`. This **works correctly** — one chunk delivered, stream closes normally, `onDidEndTerminalShellExecution` fires as expected (~11s, no signal loss). This isolates the trigger to either (a) an embedded literal newline within the group, or (b) subshell (`( )`) semantics specifically — not brace-grouping in general.

## Impact

Any extension using the shell-integration API to track command completion (Copilot Chat's terminal tool, and third-party AI coding extensions like Cline/Roo Code forks) can hang indefinitely waiting for a signal that will never arrive, for any command a user or model happens to phrase as a multi-line script or a subshell. We've had to ship a defensive workaround (self-finalizing on the `]633;D` marker text within accumulated stream data, when present) but this specific failure mode has **no marker at all** to detect, so no application-level workaround exists for it.

## Related Issues
- #316556 — `read()` stream never ends for multi-line commands
- #250764 — `onDidEndTerminalShellExecution` not received for commands with newlines
- #254724 — shell integration hangs with subshell command sequences (`(echo a ; sleep 1)`)

This report adds: (1) a case where **zero data at all** is delivered, not just a delayed/missing end marker, and (2) direct evidence of corrupted command-line parsing (the `commandLine.value`/`command` field containing command output) as a likely root cause.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.