plandex-ai/plandex · error

failed to open keyboard: %s

Error message

failed to open keyboard: %s

What it means

GetUserKeyInput wraps failures from the eiannone/keyboard library when putting the terminal into raw mode (keyboard.Open). Without raw mode the library cannot read single keystrokes, so confirm-style prompts cannot function.

Source

Thrown at app/cli/term/prompt.go:73

	return res, nil
}

func GetUserPasswordInput(msg string) (string, error) {
	disableBracketedPaste()
	defer enableBracketedPaste()

	res, err := prompt.New().Ask(msg).Input("", input.WithEchoMode(input.EchoPassword))

	if err != nil && err.Error() == "user quit prompt" {
		os.Exit(0)
	}

	return res, err
}

func GetUserKeyInput() (rune, keyboard.Key, error) {
	if err := keyboard.Open(); err != nil {
		return 0, 0, fmt.Errorf("failed to open keyboard: %s", err)
	}
	defer func() {
		_ = keyboard.Close()
	}()

	char, key, err := keyboard.GetKey()
	if err != nil {
		return 0, 0, fmt.Errorf("failed to read keypress: %s", err)
	}

	return char, key, nil
}

func ConfirmYesNo(fmtStr string, fmtArgs ...interface{}) (bool, error) {
	color.New(ColorHiMagenta, color.Bold).Printf(fmtStr+" (y)es | (n)o", fmtArgs...)
	color.New(ColorHiMagenta, color.Bold).Print("> ")

	char, key, err := GetUserKeyInput()

View on GitHub (pinned to e2d772072e)

Solutions

  1. Run the command in a standard interactive terminal (xterm, iTerm, Windows Terminal)
  2. Ensure stdin is a real TTY, not a pipe or /dev/null
  3. On Windows, use a terminal that supports console input or a build with proper console support
  4. Avoid running confirm-prompt commands under non-interactive automation; use flags that skip prompts

Example fix

// before
printf "n\n" | plandex apply
// after
plandex apply --yes   # or run interactively and press y/n
Defensive patterns

Strategy: fallback

Validate before calling

fi, _ := os.Stdin.Stat()
ttyOK := (fi.Mode() & os.ModeCharDevice) != 0 // if false, keyboard.Open will likely fail

Try / catch

char, key, err := term.GetUserKeyInput()
if err != nil {
    // fallback: read a full line from a buffered reader instead of raw keyboard
    line, _ := bufio.NewReader(os.Stdin).ReadString('\n')
    key = normalizeToKey(line)
}

Prevention

When it happens

Trigger: keyboard.Open() fails: stdin is not a TTY, the terminal can't be switched to raw mode (restricted environment, Windows console without cgo build of the lib, unsupported terminal), or the device file can't be opened.

Common situations: Running commands under piped stdin in CI, inside some IDE terminals or Windows shells lacking console support, or in sandboxed/container environments without a TTY.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/82d197d27be22305. Report an issue: GitHub.