fix: silence /dev/tty redirect errors when no tty is attached - #83
Conversation
Redirections are applied left to right, so `> /dev/tty 2>/dev/null` opens /dev/tty while stderr is still the inherited one. When /dev/tty is unavailable the shell's "No such device or address" message escapes to stderr before 2>/dev/null takes effect. Swapping the order to `2>/dev/null > /dev/tty` mutes stderr first, so the failed open is silent. Exit status and the JSON fallback path are unchanged. This surfaces on Windows/Git Bash, where the Stop hook has no controlling terminal and every turn prints: emit-terminal-sequence.sh: line 82: /dev/tty: No such device or address Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PWTCPGfRHRHJxiuXUjxP6X
There was a problem hiding this comment.
Overview
Correctly fixes the redirection-order bug: swapping to 2>/dev/null > /dev/tty mutes stderr before the /dev/tty open is attempted, so the "No such device or address" message no longer escapes when no controlling terminal is attached, with exit status unchanged (verified below). No blocking issues found.
Concerns
- Neither changed script has a regression test.
plugins/warp/tests/test-hooks.shexercisesemit_terminal_sequencebut never asserts stderr is empty when/dev/ttyopen fails, so this exact bug (and any regression of it) would not be caught by CI. A test that runs the "unknown version" fallback (or the old-version branch) with stderr captured and asserts it's empty would cover this.
Verdict
Checks: build n/a (shell scripts), tests pass (existing suite unaffected by this change), CI not yet run (fork PR pending workflow approval), visual proof n/a
Found: 0 critical, 1 important, 0 suggestions, 0 nits
Reproduced the claimed behavior in a detached (no controlling terminal) shell: old ordering leaks bash: line 1: /dev/tty: No such device or address to stderr with exit 1; new ordering produces empty stderr with the same exit 1.
Problem
On Windows / Git Bash the Stop hook runs without a controlling terminal, so every turn prints:
The
2>/dev/nullwas meant to suppress this, but shell redirections are applied left to right. In> /dev/tty 2>/dev/null, the shell opens/dev/ttywhile stderr is still the inherited one, so the failed-open message escapes before2>/dev/nulltakes effect.Fix
Swap the order to
2>/dev/null > /dev/ttyso stderr is muted before the open is attempted.Exit status is unchanged, so the
ifinemit_terminal_sequencestill falls through to the JSON path correctly.Verification
All three branches of
emit_terminal_sequenceexercised with no tty attached:CLAUDE_CODE_VERSION{"terminalSequence":"HELLO"}2.5.0(new){"terminalSequence":"HELLO"}2.0.0(old, tty path)bash -npasses on both files. Same one-line change applied tolegacy/warp-notify.sh, which has the identical pattern.🤖 Generated with Claude Code
https://claude.ai/code/session_01PWTCPGfRHRHJxiuXUjxP6X