charmbracelet/bubbletea · error

error getting console mode: %w

Error message

error getting console mode: %w

What it means

After raw mode succeeded, the Windows initInput() path reads the console input mode with windows.GetConsoleMode on stdin before enabling ENABLE_VIRTUAL_TERMINAL_INPUT. This error means that read failed — the handle is no longer a valid console input buffer between the MakeRaw and GetConsoleMode calls. It aborts Program.Run().

Source

Thrown at tty_windows.go:27

	"github.com/charmbracelet/x/term"
	"golang.org/x/sys/windows"
)

func (p *Program) initInput() (err error) {
	// Save stdin state and enable VT input
	// We also need to enable VT
	// input here.
	if f, ok := p.input.(term.File); ok && term.IsTerminal(f.Fd()) {
		p.ttyInput = f
		p.previousTtyInputState, err = term.MakeRaw(p.ttyInput.Fd())
		if err != nil {
			return fmt.Errorf("error making terminal raw: %w", err)
		}

		// Enable VT input
		var mode uint32
		if err := windows.GetConsoleMode(windows.Handle(p.ttyInput.Fd()), &mode); err != nil {
			return fmt.Errorf("error getting console mode: %w", err)
		}

		if err := windows.SetConsoleMode(windows.Handle(p.ttyInput.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
			return fmt.Errorf("error setting console mode: %w", err)
		}
	}

	// Save output screen buffer state and enable VT processing.
	if f, ok := p.output.(term.File); ok && term.IsTerminal(f.Fd()) {
		p.ttyOutput = f
		p.previousOutputState, err = term.GetState(f.Fd())
		if err != nil {
			return fmt.Errorf("error getting terminal state: %w", err)
		}

		var mode uint32
		if err := windows.GetConsoleMode(windows.Handle(p.ttyOutput.Fd()), &mode); err != nil {
			return fmt.Errorf("error getting console mode: %w", err)

View on GitHub (pinned to 351d2159f8)

Solutions

  1. Reproduce with a plain interactive console first — if it only fails under your harness, the harness is closing or detaching the console handle.
  2. Keep the ConPTY alive for the whole Program.Run() lifetime in integration tests (do not close the pty right after spawning).
  3. Defer program start until the console is fully attached (e.g. after windows.FreeConsole/AttachConsole sequences complete).
  4. As a fallback for non-interactive runs, use tea.WithInput(nil) plus tea.WithoutRenderer().

Example fix

// before: test closes the pty immediately after starting the program
go func() { p.Run() }()
pty.Close() // console handle invalidated mid-init -> error getting console mode

// after: keep the pty alive until the program exits
done := make(chan struct{})
go func() { defer close(done); p.Run() }()
<-done
pty.Close()
Defensive patterns

Strategy: try-catch

Validate before calling

var mode uint32
if err := windows.GetConsoleMode(windows.Handle(os.Stdin.Fd()), &mode); err != nil {
    // handle will also fail inside initInput; skip terminal mode
    p := tea.NewProgram(model, tea.WithInput(nil))
}

Type guard

func validConsoleHandle(fd uintptr) bool {
    var mode uint32
    return windows.GetConsoleMode(windows.Handle(fd), &mode) == nil
}

Try / catch

if _, err := p.Run(); err != nil {
    if strings.Contains(err.Error(), "error getting console mode") && runtime.GOOS == "windows" {
        // stdin console handle died mid-init: fix the harness that closes the ConPTY early
        log.Fatal("console input handle invalidated during startup; keep the ConPTY open for the program lifetime")
    }
    log.Fatalf("run: %v", err)
}

Prevention

When it happens

Trigger: Called immediately after term.MakeRaw(p.ttyInput.Fd()) succeeds; fails when the console handle is invalidated concurrently (window closed, ConPTY terminated, handle freed by a parent process) or when fd conversion yields an invalid windows.Handle.

Common situations: A test harness or parent process closes the ConPTY while the program is starting, a scheduler/service context with a half-attached console, and races during process shutdown (Ctrl+C while the program initializes).

Related errors


AI-assisted analysis of charmbracelet/bubbletea@351d2159f8 (2026-08-15). Data as JSON: /api/errors/553c94fd895e6a50. Report an issue: GitHub.