Skip to content

fix(warp): Windows Git Bash/MSYS2 — probe tty, preserve paths, degrade without jq - #88

Open
jacek4yang wants to merge 1 commit into
warpdotdev:mainfrom
jacek4yang:fix/windows-msys2-tty-jq
Open

jacek4yang wants to merge 1 commit into
warpdotdev:mainfrom
jacek4yang:fix/windows-msys2-tty-jq

Conversation

@jacek4yang

@jacek4yang jacek4yang commented Sep 15, 2026

Copy link
Copy Markdown

Problem

On Windows 11 + Git Bash/MSYS2 the Warp hooks break in three ways, all reproduced locally:

  1. Every hook writes an error to stderr, which Claude Code surfaces to the user:
    .../emit-terminal-sequence.sh: line 82: /dev/tty: No such device or address
    
  2. cwd and transcript_path are silently rewritten in the payload. Git Bash converts path-like arguments before jq sees them, so /Users/alice/my-project became D:/.../Users/alice/my-project and /tmp/transcript.jsonl became C:/Users/.../Temp/transcript.jsonl.
  3. A missing jq surfaces as jq: command not found on stderr.

Failure 2 is also why tests/test-hooks.sh reports 55 passed / 2 failed on Windows today (noted as "unrelated" in #57).

Fix

Three small changes, 44 lines of script across 3 files:

  • _tty_write() (emit-terminal-sequence.sh) — writes to /dev/tty with stderr redirected before the open: printf '%s' "$1" 2>/dev/null > /dev/tty. Redirections apply left to right, so the old > /dev/tty 2>/dev/null opened the device while stderr was still inherited and leaked the error. Probing and writing in one call also means the write stays safe if the tty goes away in between. A MSYS2 /dev/tty node exists but returns ENXIO on a detached process, so node existence alone is not a usable check.
  • MSYS2_ARG_CONV_EXCL='*' (build-payload.sh) — stops Git Bash rewriting path-like arguments. jq treats them as opaque strings. No-op on Linux/macOS.
  • jq guardsbuild_payload returns an empty body (callers read that as "no notification") and emit_terminal_sequence returns early. build-payload.sh is sourced, so it returns rather than exits.

Linux/macOS behavior is unchanged.

Verification

Windows 11, Git Bash/MSYS2 (MINGW64_NT-10.0-26200, bash 3.6.10):

Check Result
bash tests/test-hooks.sh 74 passed / 0 failed (was 55/2)
stderr from the whole suite 0 bytes
7 hooks detached, no controlling tty 0 bytes stderr each
7 hooks with jq removed from PATH exit 0, 0 bytes stderr each
version branches unset / 2.1.100 / 2.1.141 / 2.1.999 all 0 bytes stderr
POSIX + Windows-style paths through --arg/--argjson preserved exactly
bash -n on all scripts passes

Tests use the existing assert_eq/assert_json_field helpers and assert externally visible behavior: exact payload field values, exit status, and zero stderr.

Mutation testing (each retained test detects its regression)
  • Remove MSYS2_ARG_CONV_EXCL4 failures (incl. the 2 pre-existing ones)
  • Revert _tty_write to > /dev/tty 2>/dev/null10 failures
  • Remove the jq guard → 1 failure

Running the new tests against unmodified main fails 16 of 17, so none are vacuous. One assertion is a deliberate control: it checks that the old unguarded pattern does leak on a host with no usable tty, so the zero-stderr assertions cannot pass for the wrong reason.

Existing PRs

Happy to drop this in favor of any of the above if a maintainer prefers to land one first; the MSYS2 path preservation (MSYS2_ARG_CONV_EXCL) is the piece not covered elsewhere.

@jacek4yang jacek4yang changed the title fix(warp): probe tty writability and preserve paths on Windows MSYS2 fix(warp): Windows Git Bash/MSYS2 — probe tty, preserve paths, degrade without jq Sep 15, 2026
@jacek4yang
jacek4yang force-pushed the fix/windows-msys2-tty-jq branch from e1d63c4 to 7c7cfc1 Compare September 15, 2026 14:27
@jacek4yang

Copy link
Copy Markdown
Author

Squashed to a single commit and trimmed to a 44-line script diff (3 files) plus tests.

Verified on Windows 11 + Git Bash/MSYS2: 74 passed / 0 failed (was 55 passed / 2 failed on main), detached hooks emit zero stderr, and the two pre-existing Windows path-preservation failures are fixed. No Linux/macOS behavior change.

…q fallback

Three independent failures on Windows Git Bash/MSYS2, all reproduced locally:

1. Every hook leaks an error to stderr, which Claude Code surfaces to the user:
     emit-terminal-sequence.sh: line 82: /dev/tty: No such device or address
   Shell redirections apply left to right, so `> /dev/tty 2>/dev/null` opens the
   device while stderr is still inherited and the failure escapes before
   2>/dev/null takes effect. A MSYS2 /dev/tty node also exists but returns ENXIO
   on a detached process, so checking for the node is not enough.

2. cwd and transcript_path are silently rewritten. Git Bash converts path-like
   arguments before jq sees them: "/Users/alice/my-project" became
   "D:/.../Users/alice/my-project" and "/tmp/transcript.jsonl" became
   "C:/Users/.../Temp/transcript.jsonl". This is also why test-hooks.sh reports
   55 passed / 2 failed on Windows today.

3. A missing jq surfaces as "jq: command not found" on stderr.

Fixes:

- Add _tty_write(), which redirects stderr before opening /dev/tty
  (`printf '%s' "$1" 2>/dev/null > /dev/tty`). Probing and writing in one call
  keeps the write safe if the tty disappears in between.
- Set MSYS2_ARG_CONV_EXCL='*' on the jq calls so path-like arguments survive.
  jq treats them as opaque strings; ignored on Linux/macOS.
- Guard jq in build_payload (sourced, so `return` not `exit`) and in
  emit_terminal_sequence, both degrading silently.

Tests assert externally visible behavior: exact payload path values, exit
status, and zero stderr for all seven hooks and all three version branches when
detached. Mutation-checked: removing MSYS2_ARG_CONV_EXCL fails 4 tests,
reverting _tty_write to the unsafe order fails 10, removing the jq guard fails 1.
Run against unmodified main, 16 of 17 new assertions fail.

Windows 11 + Git Bash: 74 passed / 0 failed (was 55/2), 0 bytes stderr.
@jacek4yang
jacek4yang force-pushed the fix/windows-msys2-tty-jq branch from 7c7cfc1 to 3c1fb3d Compare September 15, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant