junegunn/fzf · error
invalid popup option: ${arg} (expected: [center|top|bottom|l
Error message
invalid popup option: ${arg} (expected: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]][,border-native]) What it means
Returned by startHttpServer (src/server.go:109) when net.Listen('tcp', host:port) fails while starting fzf's --listen TCP server. This is the standard bind failure for fzf's server mode: the address cannot be bound because another process owns the port, the port is privileged, or the host part cannot resolve/bind. The OS error is flattened into the formatted message, so check the port separately.
Source
Thrown at src/options.go:430
}
}
}
return shape
}
func defaultTmuxOptions(index int) *tmuxOptions {
return &tmuxOptions{
position: posCenter,
width: sizeSpec{50, true},
height: sizeSpec{50, true},
index: index}
}
func parseTmuxOptions(arg string, index int) (*tmuxOptions, error) {
var err error
opts := defaultTmuxOptions(index)
tokens := splitRegexp.Split(arg, -1)
errorToReturn := errors.New("invalid popup option: " + arg + " (expected: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]][,border-native])")
if len(tokens) == 0 || len(tokens) > 4 {
return nil, errorToReturn
}
for i, token := range tokens {
if token == "border-native" {
tokens = append(tokens[:i], tokens[i+1:]...) // cut the 'border-native' option
opts.border = true
break
}
}
// Defaults to 'center'
first := "center"
if len(tokens) > 0 {
first = tokens[0]
}
View on GitHub (pinned to bd4efa277b)
Solutions
- Find what holds the port and stop it or pick another: `ss -ltnp 'sport = :6266'` or `lsof -iTCP:6266 -sTCP:LISTEN`, then kill that process or rerun fzf with a free port.
- Use --listen 0 to let the kernel assign a free ephemeral port; fzf reports the chosen port via the shell integration ($FZF_PORT) so a fixed port is often unnecessary.
- If a low port (<1024) is required, run with elevated privileges or pick a port above net.ipv4.ip_unprivileged_port_start.
- Verify the host part of the address is a local IP/hostname of this machine (`ip addr`); use localhost or omit the host for loopback-only listening.
- Ensure FZF_API_KEY is set when binding a non-local address, otherwise you will hit the earlier 'FZF_API_KEY is required' error first.
Example fix
# before fzf --listen 6266 # port already bound by a previous fzf # after # option A: kill the previous listener kill $(lsof -t -iTCP:6266 -sTCP:LISTEN) fzf --listen 6266 # option B: let fzf pick a free port (shell integration exports $FZF_PORT) fzf --listen 0
Defensive patterns
Strategy: validation
Validate before calling
// Go caller: check the TCP port is free before spawning fzf
func portAvailable(host string, port int) bool {
if port == 0 {
return true // ephemeral, kernel picks
}
ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return false
}
ln.Close()
return true
}
if !portAvailable("localhost", 6266) {
// choose another port or use --listen 0
} Type guard
func isTcpListenFailure(stderr string) bool {
return strings.Contains(stderr, "failed to listen on ") &&
!strings.Contains(stderr, ".sock")
} Try / catch
// shell: retry on a fresh port when the fixed port is taken
fzf --listen 6266 2>err.log || {
grep -q "failed to listen on" err.log && exec fzf --listen 0
} Prevention
- Prefer --listen 0 and read the assigned port from fzf's shell integration ($FZF_PORT) instead of hardcoding ports.
- Pick ports from the dynamic/ephemeral or private ranges (49152-65535) to dodge dev servers.
- In long-lived integrations, health-check and restart the fzf server; kill it on EXIT to release the port.
When it happens
Trigger: Running fzf --listen PORT (or host:PORT) when another process already binds that TCP port (another fzf server, a web app, anything); using a port < 1024 as a non-root user; specifying a host in the --listen address that does not correspond to a local interface (e.g. --listen 203.0.113.5:6266); or the ephemeral/accepted-connection state keeping a port reserved (SO_REUSEADDR is not set, TIME_WAIT). Note: binding a non-local host additionally requires FZF_API_KEY to be set (checked earlier at src/server.go:85-87).
Common situations: Two fzf instances configured with the same fixed --listen port in shell rc files or editor plugins; choosing a port already used by a dev server (3000, 8080...); Linux net.ipv4.ip_unprivileged_port_start restrictions for low ports; typo'd host in the listen address; containers where the requested port is already mapped by another process.
Related errors
- permission denied: ${path}
- not a valid integer: ${str}
- invalid history file: ${e.Error()}
- not a valid number: ${str}
- invalid format: ${str}
AI-assisted analysis of junegunn/fzf@bd4efa277b (2026-08-15).
Data as JSON: /api/errors/64634a5969b31f42.
Report an issue: GitHub.