NousResearch/hermes-agent · error · RuntimeError
Copilot ACP process did not expose stdin/stdout pipes.
Error message
Copilot ACP process did not expose stdin/stdout pipes.
What it means
The Copilot ACP subprocess spawned successfully but did not provide usable stdin/stdout pipes, which the JSON-RPC protocol requires. Hermes kills the process and raises this RuntimeError. In practice this only occurs with an exotic spawn configuration (e.g. a wrapper that closes or redirects standard streams) since Popen is called with explicit PIPEs.
Source
Thrown at agent/copilot_acp_client.py:529
[self._acp_command] + self._acp_args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True, encoding='utf-8', errors='replace',
bufsize=1,
cwd=self._acp_cwd,
env=_build_subprocess_env(),
creationflags=windows_hide_flags(),
)
except FileNotFoundError as exc:
raise RuntimeError(
f"Could not start Copilot ACP command '{self._acp_command}'. "
"Install GitHub Copilot CLI or set HERMES_COPILOT_ACP_COMMAND/COPILOT_CLI_PATH."
) from exc
if proc.stdin is None or proc.stdout is None:
proc.kill()
raise RuntimeError("Copilot ACP process did not expose stdin/stdout pipes.")
self.is_closed = False
with self._active_process_lock:
self._active_process = proc
inbox: queue.Queue[dict[str, Any]] = queue.Queue()
stderr_tail: deque[str] = deque(maxlen=40)
def _stdout_reader() -> None:
if proc.stdout is None:
return
for line in proc.stdout:
try:
inbox.put(json.loads(line))
except Exception:
inbox.put({"raw": line.rstrip("\n")})
def _stderr_reader() -> None:View on GitHub (pinned to c896c09c42)
Solutions
- Point HERMES_COPILOT_ACP_COMMAND directly at the real copilot binary, not a wrapper that touches stdio.
- Verify manually that the binary speaks line-delimited JSON-RPC on stdio: echo '<initialize request>' | copilot ... (it should respond on stdout).
- Update Copilot CLI in case of a version-specific stdio bug.
Defensive patterns
Strategy: validation
Validate before calling
# Smoke-test the command speaks stdio JSON-RPC before wiring it in: import subprocess p = subprocess.run([cmd, '--help'], capture_output=True, text=True, timeout=10) assert p.returncode == 0, p.stderr # a healthy binary at least runs
Try / catch
try:
client = CopilotACPClient(...)
except RuntimeError as e:
if 'did not expose stdin/stdout pipes' in str(e):
# replace stdio-touching wrapper with the direct binary
... Prevention
- Point HERMES_COPILOT_ACP_COMMAND at the real binary, not a wrapper
- Never wrap the ACP command in tools that redirect or close stdio
- Test the binary manually with a piped JSON-RPC line first
When it happens
Trigger: HERMES_COPILOT_ACP_COMMAND points at a wrapper script/binary that closes stdin/stdout, or a platform-level spawn anomaly where the PIPE file descriptors are not available (proc.stdin/proc.stdout is None).
Common situations: A custom ACP shim command that daemonizes or redirects stdio; unusual shells or process supervisors interfering with inherited file descriptors. Very rare on standard setups.
Related errors
- Copilot ACP process exited early: {stderr_text}
- ACP file-system paths must be absolute.
- Path '{resolved}' is outside the session cwd '{root}'.
- Could not start Copilot ACP command '{self._acp_command}'. I
- Copilot ACP {method} failed: {err.get('message') or err}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/cbb8dc006c15b1ec.
Report an issue: GitHub.