{"record":{"id":"9d675dfae38a7e8c","repo":"charmbracelet/bubbletea","slug":"bubbletea-error-opening-tty-w","errorCode":null,"errorMessage":"bubbletea: error opening TTY: %w","messagePattern":"bubbletea: error opening TTY: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tea.go","lineNumber":1014,"sourceCode":"\t// Initialize context and teardown channel.\n\tp.handlers = channelHandlers{}\n\tcmds := make(chan Cmd)\n\n\tp.finished = make(chan struct{})\n\tdefer func() {\n\t\tclose(p.finished)\n\t}()\n\n\tdefer p.cancel()\n\n\tif p.disableInput {\n\t\tp.input = nil\n\t} else if p.input == nil {\n\t\tp.input = os.Stdin\n\t\tif !term.IsTerminal(os.Stdin.Fd()) {\n\t\t\tttyIn, _, err := OpenTTY()\n\t\t\tif err != nil {\n\t\t\t\treturn p.initialModel, fmt.Errorf(\"bubbletea: error opening TTY: %w\", err)\n\t\t\t}\n\t\t\tp.input = ttyIn\n\t\t}\n\t}\n\n\t// Handle signals.\n\tif !p.disableSignalHandler {\n\t\tp.handlers.add(p.handleSignals())\n\t}\n\n\t// Recover from panics.\n\tif !p.disableCatchPanics {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\treturnErr = fmt.Errorf(\"%w: %w\", ErrProgramKilled, ErrProgramPanic)\n\t\t\t\tp.recoverFromPanic(r)\n\t\t\t}\n\t\t}()","sourceCodeStart":996,"sourceCodeEnd":1032,"githubUrl":"https://github.com/charmbracelet/bubbletea/blob/351d2159f8d8a85613aa2a6e98c8c63df3c98623/tea.go#L996-L1032","documentation":"Returned by Program.Run when input is not disabled, no custom input was provided, stdin is not a terminal, and opening the controlling TTY (via OpenTTY -> uv.OpenTTY, i.e. /dev/tty) fails. Bubble Tea tries this fallback so programs launched with redirected stdin still get keyboard input from the terminal. The error wraps the underlying failure (usually 'no such device' or 'permission denied' on /dev/tty).","triggerScenarios":"Running with stdin piped/redirected (app < input.txt) or in environments with no controlling terminal (cron, CI, some daemons, `go test` without a pty) where /dev/tty cannot be opened; headless containers or chroots without a TTY device.","commonSituations":"CI pipelines or unit tests invoking the program; `echo | mytui` or `mytui < file`; running under systemd/cron where there is no controlling terminal; minimal Docker images lacking /dev/tty; programs that are TUI-first but sometimes run non-interactively.","solutions":["Pass explicit input when stdin is not the keyboard: tea.WithInput(os.Stdin) or a custom reader","Disable input entirely for non-interactive runs: tea.WithInput(nil) (or WithAltScreen plus WithoutSignalHandler for pure output mode)","Detect the environment first with term.IsTerminal(os.Stdin.Fd()) and branch TUI vs plain output","In tests, allocate a pty (creack/pty) or always use WithInput(bytes.Buffer)"],"exampleFix":"// before\np := tea.NewProgram(m)\n_, err := p.Run() // fails in CI: cannot open /dev/tty\n\n// after\nvar in io.Reader = os.Stdin\nif !term.IsTerminal(os.Stdin.Fd()) {\n    in = nil // or a bytes.Buffer of scripted keys\n}\np := tea.NewProgram(m, tea.WithInput(in))\n_, err := p.Run()","handlingStrategy":"validation","validationCode":"// Decide interactivity before building the program:\nfunc isInteractive() bool {\n    return term.IsTerminal(os.Stdin.Fd()) || canOpenTTY()\n}\nfunc canOpenTTY() bool {\n    f, err := tea.OpenTTY()\n    if err != nil { return false }\n    _ = f.Close()\n    return true\n}","typeGuard":"func isTTYOpenFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"error opening TTY\")\n}","tryCatchPattern":"_, err := p.Run()\nif isTTYOpenFailure(err) {\n    // retry headless\n    p2 := tea.NewProgram(m, tea.WithInput(nil), tea.WithoutRenderer())\n    _, err = p2.Run()\n}\nif err != nil { log.Fatal(err) }","preventionTips":["Branch on term.IsTerminal(os.Stdin.Fd()) at startup","In CI/tests, always pass tea.WithInput with a reader or nil","Use creack/pty to allocate real terminals in tests","For daemons, never construct a full TUI program"],"tags":["bubbletea","tty","stdin","headless","ci","go"],"backgroundTag":null,"analyzedSha":"351d2159f8d8a85613aa2a6e98c8c63df3c98623","analyzedAt":"2026-08-15T10:48:22.366Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}