# How the Agent-Reach Probe Module Verifies Command Executability Beyond shutil.which

> Agent-Reach probe module verifies command executability beyond shutil.which by running a live subprocess. Detect broken shebangs, missing interpreters, and shell exit codes 126/127.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: internals
- Published: 2026-06-24

---

**The Agent-Reach probe module validates command health by executing a live subprocess with `--version` after locating the binary with `shutil.which`, enabling it to detect broken shebangs, missing interpreters, and specific shell exit codes (126/127) that simple path checks miss.**

The `agent_reach.probe` module in the Panniantong/Agent-Reach repository provides robust command verification for the **doctor** diagnostic tool. While standard library functions like `shutil.which` only verify file existence, the probe module performs live execution tests to distinguish between missing, broken, timeout, and functioning commands.

## The Limitation of Path Checks: Why Execution Testing Matters

Simple path resolution using `shutil.which` confirms that a file exists and has executable permissions, but it cannot verify that the command will actually run. Broken shebangs, missing interpreters, or corrupted installations often pass static checks only to fail at runtime. The probe module bridges this gap by moving from static analysis to dynamic verification.

## How the Probe Module Verifies Command Executability

In [`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py), the `probe_command` function implements a multi-stage verification process that extends beyond basic path resolution.

### Initial Path Resolution

The verification begins with a standard path lookup at lines 63-65. If `shutil.which` returns `None`, the function immediately returns a `missing` status, avoiding unnecessary subprocess overhead for absent commands.

### Live Execution and Failure Mode Detection

When a path is found, the module executes the command with a harmless `--version` argument inside a sandboxed subprocess using the UTF-8-compatible environment from [`agent_reach/utils/process.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/utils/process.py). This execution phase distinguishes between several distinct failure modes that path checks cannot detect.

## Detecting Broken Installations and Execution Failures

The probe module catches specific failure modes through exception handling and exit code analysis:

### Shebang and Interpreter Errors

When a file exists but the interpreter defined in its shebang cannot be launched, `subprocess.run` raises `FileNotFoundError` or `OSError`. The code catches these exceptions at lines 89-94 and returns a `broken` status with a reinstall hint, indicating the installation is present but non-functional.

### Shell Exit Code Analysis

The module defines specific broken exit codes at lines 24-25:

```python
_BROKEN_EXIT_CODES = (126, 127)

```

After successful process launch, the return code is checked against this tuple. Code **126** indicates the command was found but is not executable, while **127** indicates the command was not found by the shell. These trigger a `broken` status (lines 100-103) with actionable repair guidance.

### Timeout and Error States

The subprocess runs with a configurable timeout (default 10 seconds). If `subprocess.TimeoutExpired` is raised at lines 94-96, the probe returns a `timeout` status. For any other non-zero exit code not in the broken list, the module returns an `error` status containing the combined stdout/stderr output (lines 100-103).

## Resilient Verification with Retry Logic

The `probe_command` function implements intelligent retry logic at lines 68-76. The loop executes the command up to `retries + 1` times, but stops early if the status is `missing` or `broken`. This optimization recognizes that file absence and interpreter errors are permanent failures, while transient network-related commands might recover on subsequent attempts.

## User Guidance and Reinstall Hints

When a broken state is detected, the helper function `reinstall_hint` (lines 38-44) constructs user-friendly repair messages. These hints suggest specific commands like `uv tool install --force` or `pipx reinstall` to help users fix corrupted environments without manual troubleshooting.

## Integration with the Doctor Diagnostic Tool

The [`agent_reach/doctor.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/doctor.py) file consumes `probe_command` to report health status for each channel in the system. By leveraging the probe module's detailed status distinctions—`missing`, `broken`, `timeout`, `error`, and `ok`—the doctor tool provides precise diagnostics that guide users toward specific fixes rather than generic "command not found" errors.

## Practical Implementation Examples

```python
from agent_reach.probe import probe_command, reinstall_hint

# Simple health check – most channels call this internally

result = probe_command("ffmpeg")                # uses default args ["--version"]

print(result.status)   # "ok", "missing", "broken", "timeout", or "error"

print(result.output)   # version info if ok

print(result.hint)     # guidance if broken

# Custom check with a different command and explicit timeout

result = probe_command(
    "mytool",
    args=["--status"],
    timeout=5,
    retries=1,
    package="mytool-pkg"
)

if not result.ok:
    if result.status == "broken":
        print("Installation appears broken:")
        print(result.hint)   # shows reinstall suggestions

    elif result.status == "missing":
        print("Command not found on PATH.")
    elif result.status == "timeout":
        print("Command hung – consider increasing the timeout.")
    else:
        print(f"Error: {result.output}")

```

## Summary

- The probe module validates commands through **live execution**, not just path existence, as implemented in [`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py).
- It detects **broken shebangs** and missing interpreters via `FileNotFoundError` and `OSError` handling at lines 89-94.
- Specific shell exit codes **126 and 127** are explicitly mapped to broken status at lines 24-25.
- A **configurable timeout** prevents hanging on slow or unresponsive commands (lines 94-96).
- **Intelligent retries** skip permanent failures (missing/broken) while allowing transient recovery (lines 68-76).
- **Reinstall hints** provide actionable repair guidance when installations are corrupted (lines 38-44).

## Frequently Asked Questions

### How does the probe module differ from shutil.which?

While `shutil.which` only verifies that a file exists at a path and has executable permissions, the probe module executes the command with `--version` to verify the interpreter loads correctly and the runtime environment is functional. This catches broken symlinks, missing shared libraries, and corrupted shebangs that pass simple path checks.

### What exit codes indicate a broken installation in Agent-Reach?

The module specifically checks for exit codes **126** (command found but not executable) and **127** (command not found) as defined in the `_BROKEN_EXIT_CODES` tuple at lines 24-25 of [`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py). These codes trigger the `broken` status with reinstall suggestions.

### How does the retry logic handle different failure types?

The retry loop in `probe_command` (lines 68-76) runs up to `retries + 1` attempts but exits immediately for `missing` or `broken` statuses. This optimization recognizes that file absence and interpreter errors are permanent failures, while `timeout` or `error` states might resolve on subsequent attempts.

### Why does the probe use --version as the default argument?

The `--version` flag is a conventional harmless argument that causes most CLI tools to print version information and exit with code 0. This provides a lightweight smoke test that verifies the runtime without side effects, though users can specify custom `args` like `--status` for specific tools via the `probe_command` function signature.