junegunn/fzf · error

invalid history file: ${e.Error()}

Error message

invalid history file: ${e.Error()}

What it means

Returned by startHttpServer (src/server.go:102) when net.Listen('unix', address.sock) fails while creating the listener for fzf's --listen UNIX socket mode. This happens after the stale-socket check, so the file was either absent or just removed; the operating system itself refused to bind a UNIX domain socket at that path. The underlying errno is unfortunately not wrapped into the message.

Source

Thrown at src/history.go:24

	"strings"
)

// History struct represents input history
type History struct {
	path     string
	lines    []string
	modified map[int]string
	maxSize  int
	cursor   int
}

// NewHistory returns the pointer to a new History struct
func NewHistory(path string, maxSize int) (*History, error) {
	fmtError := func(e error) error {
		if os.IsPermission(e) {
			return errors.New("permission denied: " + path)
		}
		return errors.New("invalid history file: " + e.Error())
	}

	// Read history file
	data, err := os.ReadFile(path)
	if err != nil {
		// If it doesn't exist, check if we can create a file with the name
		if os.IsNotExist(err) {
			data = []byte{}
			if err := os.WriteFile(path, data, 0600); err != nil {
				return nil, fmtError(err)
			}
		} else {
			return nil, fmtError(err)
		}
	}
	// Split lines and limit the maximum number of lines
	lines := strings.Split(strings.Trim(string(data), "\n"), "\n")
	if len(lines[len(lines)-1]) > 0 {

View on GitHub (pinned to bd4efa277b)

Solutions

  1. Check that the socket's parent directory exists and is writable: `mkdir -p ~/.cache/fzf && [ -w ~/.cache/fzf ]`. Prefer $XDG_RUNTIME_DIR or $TMPDIR for socket paths.
  2. Shorten the socket path if it is near 104+ characters; use something like /tmp/fzf.sock.
  3. If the path is on NFS or a network/odd filesystem, move it to a local filesystem such as /tmp or /run/user/$(id -u).
  4. If a non-socket entry (e.g. a directory) has the socket's name, remove or rename it: `rm -rf /path/fzf.sock` only if it is yours.
  5. Re-run with strace (`strace -f -e trace=bind fzf --listen ...`) to capture the exact errno if the above does not resolve it.

Example fix

# before
fzf --listen /run/fzf/main-listener-socket.sock   # /run not writable, path too long

# after
mkdir -p "${XDG_RUNTIME_DIR:-/tmp}/fzf"
fzf --listen "${XDG_RUNTIME_DIR:-/tmp}/fzf/fzf.sock"
Defensive patterns

Strategy: validation

Validate before calling

// Go caller: verify the socket path is bindable before spawning fzf
func canBindUnixSocket(sockPath string) bool {
    if len(sockPath) >= 104 { // typical sun_path limit
        return false
    }
    dir := filepath.Dir(sockPath)
    info, err := os.Stat(dir)
    if err != nil || !info.IsDir() {
        return false
    }
    // probe writability by creating and removing a temp file in dir
    f, err := os.CreateTemp(dir, ".probe-*")
    if err != nil {
        return false
    }
    f.Close()
    os.Remove(f.Name())
    return true
}

Type guard

func isUnixListenFailure(stderr string) bool {
    return strings.Contains(stderr, "failed to listen on ") &&
        strings.HasSuffix(strings.TrimSpace(strings.Split(stderr, "\n")[0]), ".sock")
}

Try / catch

// shell wrapper: fail with diagnostics instead of a bare fzf error
if ! fzf --listen "$SOCK" 2>err.log; then
    if grep -q "failed to listen on $SOCK" err.log; then
        echo "cannot bind $SOCK: check dir exists/writable, path length < 104, local fs" >&2
    fi
fi

Prevention

When it happens

Trigger: Calling fzf --listen /some/dir/fzf.sock where the parent directory does not exist, is not writable by the current user, or the path crosses a filesystem that disallows sockets (e.g. some NFS mounts, /proc, FAT). Also triggered when the path is too long (UNIX socket paths are limited to ~104-108 bytes) or a non-socket file system entry occupies the name in a way os.Stat/os.Remove could not clear (e.g. a directory with that exact name).

Common situations: Pointing --listen at a socket path inside a directory the user cannot write (e.g. /run/fzf.sock as non-root); a socket path on an NFS/overlay mount that returns EOPNOTSUPP or EACCES; typos in the directory part of the socket path; paths generated with long temp prefixes exceeding sun_path limits; containers where the chosen directory is read-only.

Related errors


AI-assisted analysis of junegunn/fzf@bd4efa277b (2026-08-15). Data as JSON: /api/errors/e307786c393524c1. Report an issue: GitHub.