OpenHands / OpenHands/software-agent-sdk

[Bug]: Windows PowerShell terminal is permanently poisoned after a terminating error or interrupt

Open
#4,963 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug priority:high ready-for-dev release-note-required tools windows
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Is there an existing issue for the same bug?
  • I have searched existing issues and this is not a duplicate.
Bug Description

On native Windows, a persistent PowerShell terminal session (WindowsTerminal)
can become permanently poisoned by a single command that ends with a
terminating error or is interrupted. After that, every subsequent command is
rejected with "the previous command is still running", so an agent cannot make
progress. This corresponds to the downstream report OpenHands/OpenHands#17198
("[Command finished execution with no output]").

Root cause is Windows-only and two-fold:

  1. The command-completion sentinel (###PS1END### metadata block) is appended
    to the same PowerShell statement as the user command, so a terminating
    error (throw, -ErrorAction Stop, $ErrorActionPreference='Stop') aborts
    the line before the sentinel runs and completion is never detected.
  2. The shell is spawned with CREATE_NEW_PROCESS_GROUP, which disables Ctrl+C
    for the shell and its children, so interrupts never reach the running
    command and child process trees survive.
Expected Behavior

After a command exits, fails, times out, or is interrupted, the terminal
recovers and the next command runs. One bad command must not poison the session.

Actual Behavior

Reproducible with a plain SDK script (no LLM), on native Windows, using
python:

python -c "from openhands.tools.terminal.definition import TerminalAction; from openhands.tools.terminal.terminal import create_terminal_session as c; s=c(work_dir='.', terminal_type='powershell', no_change_timeout_seconds=1); s.initialize(); s.execute(TerminalAction(command='Start-Sleep 120')); s.execute(TerminalAction(command='C-c', is_input=True)); print(s.execute(TerminalAction(command='Get-ChildItem')).metadata.suffix)"

On stock 1.46.0 the final line prints "...the previous command is still
running...": the session is poisoned and every later command is rejected.
throw and -ErrorAction Stop reproduce the same poison without any interrupt.

Steps to Reproduce
  1. On native Windows, run the python snippet above against
    openhands-tools 1.46.0 (or run uv run pytest tests/tools/terminal with a
    Windows-only recovery test).
  2. Observe the "previous command is still running" rejection on the command
    after the interrupt.
OpenHands SDK Version

1.46.0 (also present on 1.47.0 / current main; the terminal files are
unchanged between them).

Installation Method

Agent Server via uvx / Agent Canvas 1.17.0 on Windows 11, PowerShell 5.1.

Acceptance Criteria
  • After an interrupted or terminating-error command on the Windows
    PowerShell backend, the next command executes normally (no "previous
    command is still running").
  • Ctrl+C stops the running command and its child process tree.
  • A shell that exits or stops responding is recreated automatically before
    the next command.
  • Linux/tmux/subprocess behaviour is unchanged and existing
    tests/tools/terminal tests still pass.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at openhands.tools.terminal.definition.TerminalAction and openhands.tools.terminal.terminal.create_terminal_session, then inspect the Windows PowerShell backend and its completion sentinel and process-group handling. Reproduce the failure with the provided Start-Sleep/C-c SDK snippet and add a Windows-only recovery test under tests/tools/terminal. Done means interrupted or terminating-error commands recover, child processes stop, dead shells are recreated, and existing Linux/tmux/subprocess tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
powershell, python
Domain
cli, operating-systems, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.