MSYS path conversion breaking Windows terminal commands

NousResearch/hermes-agent · cluster 56147 · environments

Data as of

Nothing open on GitHub. Fix merged in #57006. Check it covers your case.

The short answer

0open issues
0open PRs
6items grouped
Known?
Yes. 6 related items grouped here, 2 issues already closed.
Fix event
A fix was merged to main in PR #57006. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PR #57006. That is separate from the fix event.
Still open
Nothing open on GitHub.

These issues and pull requests address MSYS/Git Bash argv path conversion mangling arguments like '/c' before they reach native Windows commands such as cmd.exe. Proposed fixes involve disabling or excluding MSYS path conversion for affected subprocesses on Windows.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #57006 fix(terminal): disable MSYS argv path conversion for Windows bash subprocesses

    merged to main

    files: tests/tools/test_local_env_windows_msys.py, tools/environments/local.py

    Show the diff
    • tests/tools/test_local_env_windows_msys.py
      @@ -24,9 +24,12 @@
       from tools.environments import local as local_mod
       from tools.environments.local import (
           LocalEnvironment,
      +    _make_run_env,
           _msys_to_windows_path,
           _resolve_safe_cwd,
      +    _sanitize_subprocess_env,
           _windows_to_msys_path,
      +    hermes_subprocess_env,
       )
       
       
      @@ -228,6 +231,53 @@ def test_valid_msys_marker_normalized_to_native(self, monkeypatch, tmp_path):
               assert env.cwd == str(new_dir)
       
       
      +# ---------------------------------------------------------------------------
      +# MSYS_NO_PATHCONV — native Windows command flags (#56700)
      +# ---------------------------------------------------------------------------
      +
      +class TestWindowsMsysPathconvDefaults:
      +    def test_make_run_env_sets_msys_no_pathconv_on_windows(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
      +        run_env = _make_run_env({})
      +        assert run_env.get("MSYS_NO_PATHCONV") == "1"
      +
      +    def test_sanitize_subprocess_env_sets_msys_no_pathconv_on_windows(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
      +        env = _sanitize_subprocess_env({})
      +        assert env.get("MSYS_NO_PATHCONV") == "1"
      +
      +    def test_hermes_subprocess_env_sets_msys_no_pathconv_on_windows(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
      +        env = hermes_subprocess_env()
      +        assert env.get("MSYS_NO_PATHCONV") == "1"
      +
      +    def test_no_pathconv_not_set_on_posix(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", False)
      +        assert "MSYS_NO_PATHCONV" not in _make_run_env({})
      +
      +    def test_respects_user_override(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
      +        run_env = _make_run_env({"MSYS_NO_PATHCONV": "0"})
      +        assert run_env.get("MSYS_NO_PATHCONV") == "0"
      +
      +    def test_msys2_arg_conv_excl_set_on_windows(self, monkeypatch):
      +        # MSYS2-proper / Cygwin bash ignore MSYS_NO_PATHCONV; they honor
      +        # MSYS2_ARG_CONV_EXCL. Both must be set on every env builder.
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
      +        assert _make_run_env({}).get("MSYS2_ARG_CONV_EXCL") == "*"
      +        assert _sanitize_subprocess_env({}).get("MSYS2_ARG_CONV_EXCL") == "*"
      +        assert hermes_subprocess_env().get("MSYS2_ARG_CONV_EXCL") == "*"
      +
      +    def test_msys2_arg_conv_excl_not_set_on_posix(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", False)
      +        assert "MSYS2_ARG_CONV_EXCL" not in _make_run_env({})
      +
      +    def test_msys2_arg_conv_excl_respects_user_override(self, monkeypatch):
      +        monkeypatch.setattr(local_mod, "_IS_WINDOWS", True)
      +        run_env = _make_run_env({"MSYS2_ARG_CONV_EXCL": "/custom"})
      +        assert run_env.get("MSYS2_ARG_CONV_EXCL") == "/custom"
      +
      +
       # ---------------------------------------------------------------------------
       # Command wrapping — native Windows cwd must be Git Bash-friendly for cd
       # ---------------------------------------------------------------------------
    • tools/environments/local.py
      @@ -383,6 +383,8 @@ def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = Non
           for _marker in _ACTIVE_VENV_MARKER_VARS:
               sanitized.pop(_marker, None)
       
      +    _apply_windows_msys_bash_env_defaults(sanitized)
      +
           return sanitized
       
       
      @@ -493,6 +495,8 @@ def hermes_subprocess_env(*, inherit_credentials: bool = False) -> dict[str, str
           for _marker in _ACTIVE_VENV_MARKER_VARS:
               env.pop(_marker, None)
       
      +    _apply_windows_msys_bash_env_defaults(env)
      +
           # Cross-session leak guard, same as the terminal spawn paths: this helper
           # copies os.environ, whose HERMES_SESSION_* mirror is a last-writer-wins
           # global under a concurrent multi-session host. A caller that re-binds the
      @@ -746,6 +750,29 @@ def _append_missing_sane_path_entries(existing_path: str) -> str:
           return ":".join(ordered_entries)
       
       
      +def _apply_windows_msys_bash_env_defaults(env: dict) -> None:
      +    """Disable MSYS argument path conversion for Git Bash subprocesses.
      +
      +    Git Bash rewrites arguments that look like Unix paths (``/FO``, ``/TN``,
      +    ``/Create``) into ``C:/.../git/FO``-style paths, which breaks native
      +    Windows commands such as ``tasklist``, ``schtasks``, and ``wmic``.  Hermes
      +    runs terminal commands through bash on Windows, so set the standard MSYS
      +    opt-out by default.  Users who need conversion can override in their env.
      +    Refs #56700.
      +
      +    ``MSYS_NO_PATHCONV`` is honored by Git for Windows bash only.  MSYS2-proper
      +    and Cygwin bash (which ``_find_bash`` can still return via the final
      +    ``shutil.which`` fallback) ignore it and honor ``MSYS2_ARG_CONV_EXCL``
      +    instead, so set both.  ``*`` disables all argv conversion — the semantic
      +    equivalent of ``MSYS_NO_PATHCONV=1``.  Also fixes ``cmd /c`` mangling
      +    (#56147).
      +    """
      +    if not _IS_WINDOWS:
      +        return
      +    env.setdefault("MSYS_NO_PATHCONV", "1")
      +    env.setdefault("MSYS2_ARG_CONV_EXCL", "*")
      +
      +
       def _path_env_key(run_env: dict) -> str | None:
           """Return the PATH env key to update without altering Windows casing.
       
      @@ -804,6 +831,8 @@ def _make_run_env(env: dict) -> dict:
           for _marker in _ACTIVE_VENV_MARKER_VARS:
               run_env.pop(_marker, None)
       
      +    _apply_windows_msys_bash_env_defaults(run_env)
      +
           return run_env
       
       

3 PRs closed without merging

From the close comments: duplicate 1 · superseded by another PR 1 · other 1

  • #56228 fix(env): inject MSYS2_ARG_CONV_EXCL=/c on Windows to prevent cmd /c silent failure · superseded by another PR → a2d49de, #57006, #56147
  • #56734 fix(terminal): disable MSYS path conversion on Windows · duplicate → #56705, #56700
  • #56783 fix(terminal): keep cmd /c from being rewritten by MSYS on Windows (#56147) · other → #56228, #56147
Closed issues · 2
  • Closed#56147[Bug]: GUI launch commands on Windows say exit_code: 0 but never actually open anything (Git Bash/MSYS rewriting /c before cmd.exe gets it)
  • Closed#56700Windows terminal: MSYS path mangling breaks native commands (tasklist, schtasks, wmic)
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.36: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
environments
Merged PRs
#57006
Closed issues
2
Attempts
3 PRs closed without merging
Fix link checked
2 Jul 2026

For your agent

The same facts, typed.