junegunn/fzf · error
failed to open %s
Error message
failed to open %s
What it means
openTty failed to obtain a usable terminal device: the candidate input is not a TTY, and fallback opens of the controlling terminal name and /dev/tty both failed. fzf needs a real terminal for its interactive UI, so this aborts before rendering.
Source
Thrown at src/tui/light_unix.go:64
func openTty(ttyDefault string, mode int) (*os.File, error) {
var in *os.File
var err error
if len(ttyDefault) > 0 {
in, err = os.OpenFile(ttyDefault, mode, 0)
}
if in == nil || err != nil || ttyDefault != DefaultTtyDevice && !util.IsTty(in) {
tty := ttyname()
if len(tty) > 0 {
if in, err := os.OpenFile(tty, mode, 0); err == nil {
return in, nil
}
}
if ttyDefault != DefaultTtyDevice {
if in, err = os.OpenFile(DefaultTtyDevice, mode, 0); err == nil {
return in, nil
}
}
return nil, errors.New("failed to open " + DefaultTtyDevice)
}
return in, nil
}
func openTtyIn(ttyDefault string) (*os.File, error) {
return openTty(ttyDefault, syscall.O_RDONLY)
}
func openTtyOut(ttyDefault string) (*os.File, error) {
return openTty(ttyDefault, syscall.O_WRONLY)
}
func (r *LightRenderer) setupTerminal() {
term.MakeRaw(r.fd())
}
func (r *LightRenderer) restoreTerminal() {
term.Restore(r.fd(), r.origState)View on GitHub (pinned to bd4efa277b)
Solutions
- Allocate a TTY: run fzf in an interactive shell or use 'script -qec "fzf" /dev/null'
- For non-interactive filtering use 'fzf --filter QUERY' which never opens the TUI
- In ssh, enable allocation with 'ssh -t'
Example fix
# before (cron, no controlling terminal) fzf # after printf 'a\nb\n' | fzf --filter a
Defensive patterns
Strategy: fallback
Validate before calling
# ensure a controlling terminal or fall back to filter mode if ! [ -t 0 ] && ! [ -e /dev/tty ]; then exec fzf --filter "$QUERY" fi fzf --query "$QUERY"
Prevention
- Use ssh -t for remote interactive sessions
- In containers, run with -t (docker exec -it) so /dev/tty exists
When it happens
Trigger: openTtyIn/openTtyOut are called when stdin/stdout is redirected (pipe/file) and either ttyname() yields nothing, the named tty cannot be opened, or opening DefaultTtyDevice (/dev/tty) fails — typical when the process has no controlling terminal.
Common situations: Piping into fzf inside cron jobs, CI pipelines, or daemons where there is no controlling terminal; ssh -T sessions without tty allocation; minimal containers lacking /dev/tty.
Related errors
- not a valid number: ${str}
- $FZF_DEFAULT_OPTS_FILE: %s
- $FZF_DEFAULT_OPTS: %s
- failed to read %s
- permission denied: ${path}
AI-assisted analysis of junegunn/fzf@bd4efa277b (2026-08-15).
Data as JSON: /api/errors/994734d5b16fcd99.
Report an issue: GitHub.