Skip to content

Fix crash on Windows: guard POSIX-only SIGWINCH trap#123

Open
freyazh wants to merge 1 commit into
tobi:mainfrom
freyazh:fix/windows-sigwinch-crash
Open

Fix crash on Windows: guard POSIX-only SIGWINCH trap#123
freyazh wants to merge 1 commit into
tobi:mainfrom
freyazh:fix/windows-sigwinch-crash

Conversation

@freyazh

@freyazh freyazh commented Jul 12, 2026

Copy link
Copy Markdown

Problem

On Windows, try crashes the instant the interactive picker opens:

try.rb:80:in 'Signal.trap': unsupported signal 'SIGWINCH' (ArgumentError)
    @old_winch_handler = Signal.trap('WINCH') { @needs_redraw = true }

SIGWINCH is POSIX-only — it isn't in the Windows signal set, so Signal.list
has no 'WINCH' key and Signal.trap('WINCH') raises ArgumentError.
TrySelector#setup_terminal traps it unconditionally, so the process dies before
the TUI renders. This hits any Windows Ruby (RUBY_PLATFORM = x64-mingw-ucrt /
mingw / mswin). The README says try "works on any system with Ruby" and the gem
declares no platform restriction, so Windows is in scope.

Fix

Trap WINCH only where the platform actually has it:

@old_winch_handler = Signal.trap('WINCH') { @needs_redraw = true } if Signal.list.key?('WINCH')

WINCH is the only signal try traps, and the restore site in
restore_terminal is already nil-guarded
(Signal.trap('WINCH', @old_winch_handler) if @old_winch_handler, with
@old_winch_handler = nil set in initialize). On Windows the guard leaves
@old_winch_handler nil, so teardown is a clean no-op and nothing regresses. On
POSIX, Signal.list.key?('WINCH') is true and behaviour is unchanged — no loss,
including live-resize redraw. Using Signal.list.key?('WINCH') rather than
Gem.win_platform? keeps it a portable "trap only if the signal exists" check.

Scope / testing

Deliberately minimal — just the crash. Verified on x64-mingw-ucrt Ruby that
Signal.list.key?('WINCH') is false and the picker now reaches
setup_terminal without raising. CI here is Linux-only (where WINCH exists),
so this path can't be exercised there; the guard is a no-op on the tested
platforms.

Follow-up (intentionally not in this PR): once the crash is gone, the
picker's input loop still assumes a POSIX console — IO.select([STDIN]),
STDIN.read_nonblock, STDIN.iflush, and IO.console.winsize (which raises
Errno::EBADF when stderr isn't a real console). Making the interactive picker
fully functional on Windows is separate work; PR #70's "full parity" attempt
took a non-Ruby route and was closed. Keeping this to the one-line crash guard so
it's easy to accept.

TrySelector#setup_terminal trapped SIGWINCH unconditionally. On Windows
Ruby (x64-mingw-ucrt) SIGWINCH isn't a supported signal, so
Signal.trap('WINCH') raises `ArgumentError: unsupported signal 'SIGWINCH'`
the instant the picker opens, aborting before the TUI renders.

Trap it only when the platform has it (`Signal.list.key?('WINCH')`). WINCH
is the only signal trapped and restore_terminal is already nil-guarded, so
teardown is unaffected; POSIX behaviour (including live-resize redraw) is
unchanged. Adds a Minitest that stubs Signal.list to assert setup_terminal
doesn't raise when WINCH is absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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