Skip to content

ShellTools.run() cannot start on Windows: /bin/bash is hardcoded as the executable 🤖🤖🤖 #110

Description

@IgorKasianenko

Describe the bug

BashSession.start() passes the literal string "/bin/bash" to asyncio.create_subprocess_exec as the executable (src/nooa/tools/_bash_session.py:164). That path does not exist on native Windows, so ShellTools.run() cannot start a session there.

This is not a regression and not a new break — it has never worked on Windows. It is filed because it is currently unreported: #95 mentions the pass_fds failure in passing, and its own body notes that its STEP 6/7 findings are not covered by any open issue. This is STEP 6's next failure.

Relationship to #98

#98 fixes the failure that currently masks this one. On main, start() fails first at:

AssertionError: pass_fds not supported on Windows

because start_new_session=True and pass_fds=(ctrl_w,) are passed unconditionally (lines 172-173). #98 correctly moves both behind a sys.platform != "win32" guard:

kwargs: dict[str, Any] = {}
if sys.platform != "win32":
    kwargs["start_new_session"] = True
    kwargs["pass_fds"] = (ctrl_w,)

That guard works — verified on a native Windows 11 host against #98's branch. But the diff leaves the "/bin/bash" argument unchanged, so the call now proceeds one step further and fails with:

FileNotFoundError: [WinError 2] The system cannot find the file specified

So #98 changes which error Windows users get, not whether ShellTools.run() works. Worth stating explicitly so #98 is not read as making the shell surface Windows-ready.

A second obstacle behind this one

Even with a bash on PATH (Git Bash, MSYS2), dropping pass_fds on Windows removes the control-channel descriptor the protocol depends on — ctrl_w is fd 3 inside bash. Making ShellTools.run() genuinely work on Windows is therefore a larger design question than substituting an executable path, which is why this issue proposes the smaller thing below.

To reproduce

On a native Windows host (no WSL), with #98's branch checked out:

import asyncio
from nooa.tools import ShellTools

asyncio.run(ShellTools().run("echo hi"))

On main the same snippet fails earlier, with the pass_fds AssertionError.

Expected behavior

Either of these would be an improvement; I'd suggest the first:

  1. Fail clearly. Raise an explicit, documented error on Windows — something like ShellTools requires a POSIX shell and is not supported on native Windows; use WSL — instead of FileNotFoundError: [WinError 2], which reads like a missing user file rather than an unsupported platform.
  2. Resolve the shell rather than hardcoding it (shutil.which("bash"), $SHELL), which also helps NixOS and other hosts where /bin/bash is not the canonical path — but this does not by itself make Windows work, because of the fd-3 control channel above.

Environment

Additional context

Same family as #84, #85, #92 — POSIX-only assumptions reachable from ordinary use. Unlike those, this one is not on the import nooa path, so the import smoke test in #108 will not catch it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions