TUI: Unhandled exceptions in subprocess.run in shell_exec.py #8882

Open
opened 2026-04-14 03:20:45 +00:00 by HAL9000 · 1 comment
Owner

Metadata

  • Commit Message: fix(tui): handle OSError and other exceptions in run_shell_command
  • Branch: bugfix/tui-shell-exec-unhandled-exceptions

Background and Context

The run_shell_command function in src/cleveragents/tui/input/shell_exec.py wraps subprocess.run in a try/except block that only catches subprocess.TimeoutExpired. This means any other exception raised by subprocess.run — such as FileNotFoundError (when the shell binary is not found), PermissionError (when the shell binary is not executable), or OSError (for other OS-level failures) — will propagate uncaught up the call stack.

Because run_shell_command is called from the TUI event loop, an unhandled exception here can crash the TUI entirely, leaving the user with no feedback and no graceful recovery path.

Current Behavior

try:
    proc = subprocess.run(
        command,
        shell=True,
        text=True,
        capture_output=True,
        timeout=timeout_seconds,
    )  # nosec B602
except subprocess.TimeoutExpired:
    return ShellResult(
        command=command,
        exit_code=124,
        stdout="",
        stderr=f"command timed out after {timeout_seconds}s",
    )

Only subprocess.TimeoutExpired is caught. Exceptions such as FileNotFoundError, PermissionError, and OSError are not handled and will propagate unhandled, potentially crashing the TUI.

Code location: src/cleveragents/tui/input/shell_exec.pyrun_shell_command function, subprocess.run call.

Expected Behavior

run_shell_command should catch all OS-level exceptions that subprocess.run can raise (e.g., OSError, which is the base class for FileNotFoundError and PermissionError) and return a structured ShellResult with a meaningful error message in stderr and a non-zero exit_code, rather than allowing the exception to propagate and crash the TUI.

Acceptance Criteria

  • run_shell_command catches OSError (and its subclasses, including FileNotFoundError and PermissionError) raised by subprocess.run and returns a ShellResult with a descriptive stderr message and a non-zero exit_code (e.g., 126 for permission denied, 127 for command not found, or a general 1 for other OS errors).
  • The TUI does not crash when subprocess.run raises an OSError.
  • Existing TimeoutExpired handling is preserved and unchanged.
  • BDD scenarios cover the new exception-handling paths (at minimum: FileNotFoundError, PermissionError, and a generic OSError).
  • All tests pass and coverage remains ≥ 97%.

Subtasks

  • Add except OSError clause to the try/except block in run_shell_command to catch FileNotFoundError, PermissionError, and other OS-level errors from subprocess.run
  • Return a structured ShellResult with an appropriate exit_code and descriptive stderr message for each caught exception type
  • Tests (Behave): Add BDD scenarios for FileNotFoundError, PermissionError, and generic OSError raised by subprocess.run
  • Tests (Robot): Add integration test verifying the TUI shell mode does not crash on OS-level subprocess failures
  • Verify coverage ≥ 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.

Automated by CleverAgents Bot
Agent: new-issue-creator

## Metadata - **Commit Message**: `fix(tui): handle OSError and other exceptions in run_shell_command` - **Branch**: `bugfix/tui-shell-exec-unhandled-exceptions` ## Background and Context The `run_shell_command` function in `src/cleveragents/tui/input/shell_exec.py` wraps `subprocess.run` in a `try/except` block that only catches `subprocess.TimeoutExpired`. This means any other exception raised by `subprocess.run` — such as `FileNotFoundError` (when the shell binary is not found), `PermissionError` (when the shell binary is not executable), or `OSError` (for other OS-level failures) — will propagate uncaught up the call stack. Because `run_shell_command` is called from the TUI event loop, an unhandled exception here can crash the TUI entirely, leaving the user with no feedback and no graceful recovery path. ## Current Behavior ```python try: proc = subprocess.run( command, shell=True, text=True, capture_output=True, timeout=timeout_seconds, ) # nosec B602 except subprocess.TimeoutExpired: return ShellResult( command=command, exit_code=124, stdout="", stderr=f"command timed out after {timeout_seconds}s", ) ``` Only `subprocess.TimeoutExpired` is caught. Exceptions such as `FileNotFoundError`, `PermissionError`, and `OSError` are not handled and will propagate unhandled, potentially crashing the TUI. **Code location**: `src/cleveragents/tui/input/shell_exec.py` — `run_shell_command` function, `subprocess.run` call. ## Expected Behavior `run_shell_command` should catch all OS-level exceptions that `subprocess.run` can raise (e.g., `OSError`, which is the base class for `FileNotFoundError` and `PermissionError`) and return a structured `ShellResult` with a meaningful error message in `stderr` and a non-zero `exit_code`, rather than allowing the exception to propagate and crash the TUI. ## Acceptance Criteria - [ ] `run_shell_command` catches `OSError` (and its subclasses, including `FileNotFoundError` and `PermissionError`) raised by `subprocess.run` and returns a `ShellResult` with a descriptive `stderr` message and a non-zero `exit_code` (e.g., `126` for permission denied, `127` for command not found, or a general `1` for other OS errors). - [ ] The TUI does not crash when `subprocess.run` raises an `OSError`. - [ ] Existing `TimeoutExpired` handling is preserved and unchanged. - [ ] BDD scenarios cover the new exception-handling paths (at minimum: `FileNotFoundError`, `PermissionError`, and a generic `OSError`). - [ ] All tests pass and coverage remains ≥ 97%. ## Subtasks - [ ] Add `except OSError` clause to the `try/except` block in `run_shell_command` to catch `FileNotFoundError`, `PermissionError`, and other OS-level errors from `subprocess.run` - [ ] Return a structured `ShellResult` with an appropriate `exit_code` and descriptive `stderr` message for each caught exception type - [ ] Tests (Behave): Add BDD scenarios for `FileNotFoundError`, `PermissionError`, and generic `OSError` raised by `subprocess.run` - [ ] Tests (Robot): Add integration test verifying the TUI shell mode does not crash on OS-level subprocess failures - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. --- **Automated by CleverAgents Bot** Agent: new-issue-creator
HAL9000 added this to the v3.7.0 milestone 2026-04-14 03:26:10 +00:00
Author
Owner

Triage Decision: VERIFIED — MoSCoW/Must Have

Real TUI reliability bug: unhandled exceptions in subprocess.run in shell_exec.py can crash the TUI shell execution feature. Shell commands can fail for many reasons (command not found, permission denied, etc.) and these must be handled gracefully.

Priority/High — Shell execution is a core TUI feature; unhandled exceptions crash the application.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Triage Decision: VERIFIED — MoSCoW/Must Have** Real TUI reliability bug: unhandled exceptions in `subprocess.run` in `shell_exec.py` can crash the TUI shell execution feature. Shell commands can fail for many reasons (command not found, permission denied, etc.) and these must be handled gracefully. **Priority/High** — Shell execution is a core TUI feature; unhandled exceptions crash the application. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#8882
No description provided.