[PR #13298] fix(win32): prevent ANSI color codes leaking on Ctrl+C exit #14604

Open
opened 2026-02-16 18:19:23 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/13298

State: open
Merged: No


Closes #13301

Summary

On Windows, pressing Ctrl+C to exit the application causes raw ANSI escape sequences (color codes) to be printed to the terminal after termination. Exiting via the exit command works cleanly without this issue.

Root Cause

The @opentui/core renderer registers signal handlers (including SIGINT) by default via addExitListeners(). On Windows, the app uses a custom win32 guard (win32InstallCtrlCGuard) that disables ENABLE_PROCESSED_INPUT so Ctrl+C is delivered as a keyboard event rather than a signal. However:

  1. SIGINT race condition: The guard polls every 100ms to enforce the flag, but Bun's runtime can momentarily re-enable ENABLE_PROCESSED_INPUT between polls. If Ctrl+C is pressed during this window, it generates a CTRL_C_EVENTSIGINT instead of a keyboard event.

  2. Bypassed exit flow: When SIGINT fires, opentui's built-in handler calls renderer.destroy() directly — bypassing the app's graceful exit function which handles process.exit(0), unguard() (console mode restoration), and worker shutdown. This results in incomplete terminal cleanup and visible ANSI escape codes.

Fix

Pass exitSignals: [] to the opentui render config. This prevents opentui from registering any signal handlers (addExitListeners() skips registration when the array is empty). Since the app already sets exitOnCtrlC: false and handles Ctrl+C through its own keyboard-event-based exit path, opentui's signal handlers are unnecessary and only introduce the race condition described above.

Changes

  • packages/opencode/src/cli/cmd/tui/app.tsx: Added exitSignals: [] to the render options

Test plan

  • Build and run on Windows
  • Press Ctrl+C to exit → no ANSI color codes printed after termination
  • Type exit to quit → still exits cleanly
  • Ctrl+C in various states (empty prompt, dialog open, mid-input) behaves correctly
**Original Pull Request:** https://github.com/anomalyco/opencode/pull/13298 **State:** open **Merged:** No --- Closes #13301 ## Summary On Windows, pressing **Ctrl+C** to exit the application causes raw ANSI escape sequences (color codes) to be printed to the terminal after termination. Exiting via the `exit` command works cleanly without this issue. ### Root Cause The `@opentui/core` renderer registers signal handlers (including `SIGINT`) by default via `addExitListeners()`. On Windows, the app uses a custom win32 guard (`win32InstallCtrlCGuard`) that disables `ENABLE_PROCESSED_INPUT` so Ctrl+C is delivered as a keyboard event rather than a signal. However: 1. **SIGINT race condition**: The guard polls every 100ms to enforce the flag, but Bun's runtime can momentarily re-enable `ENABLE_PROCESSED_INPUT` between polls. If Ctrl+C is pressed during this window, it generates a `CTRL_C_EVENT` → `SIGINT` instead of a keyboard event. 2. **Bypassed exit flow**: When SIGINT fires, opentui's built-in handler calls `renderer.destroy()` directly — bypassing the app's graceful exit function which handles `process.exit(0)`, `unguard()` (console mode restoration), and worker shutdown. This results in incomplete terminal cleanup and visible ANSI escape codes. ### Fix Pass `exitSignals: []` to the opentui render config. This prevents opentui from registering any signal handlers (`addExitListeners()` skips registration when the array is empty). Since the app already sets `exitOnCtrlC: false` and handles Ctrl+C through its own keyboard-event-based exit path, opentui's signal handlers are unnecessary and only introduce the race condition described above. ## Changes - **`packages/opencode/src/cli/cmd/tui/app.tsx`**: Added `exitSignals: []` to the render options ## Test plan - [x] Build and run on Windows - [x] Press Ctrl+C to exit → no ANSI color codes printed after termination - [x] Type `exit` to quit → still exits cleanly - [x] Ctrl+C in various states (empty prompt, dialog open, mid-input) behaves correctly
yindo added the pull-request label 2026-02-16 18:19:23 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#14604