{"record":{"id":"d0a396f5a7fb717b","repo":"junegunn/fzf","slug":"permission-denied-path","errorCode":null,"errorMessage":"permission denied: ${path}","messagePattern":"permission denied: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/history.go","lineNumber":22,"sourceCode":"\t\"errors\"\n\t\"os\"\n\t\"strings\"\n)\n\n// History struct represents input history\ntype History struct {\n\tpath     string\n\tlines    []string\n\tmodified map[int]string\n\tmaxSize  int\n\tcursor   int\n}\n\n// NewHistory returns the pointer to a new History struct\nfunc NewHistory(path string, maxSize int) (*History, error) {\n\tfmtError := func(e error) error {\n\t\tif os.IsPermission(e) {\n\t\t\treturn errors.New(\"permission denied: \" + path)\n\t\t}\n\t\treturn errors.New(\"invalid history file: \" + e.Error())\n\t}\n\n\t// Read history file\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\t// If it doesn't exist, check if we can create a file with the name\n\t\tif os.IsNotExist(err) {\n\t\t\tdata = []byte{}\n\t\t\tif err := os.WriteFile(path, data, 0600); err != nil {\n\t\t\t\treturn nil, fmtError(err)\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, fmtError(err)\n\t\t}\n\t}\n\t// Split lines and limit the maximum number of lines","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/junegunn/fzf/blob/bd4efa277b49ef34ca4025bff9b1a288e1980eff/src/history.go#L4-L40","documentation":"Thrown by startHttpServer (src/server.go:96) when fzf is started with --listen using a *.sock UNIX domain socket address and another live process is already accepting connections on that socket file. fzf probes the existing socket with net.Dial('unix', ...); if the dial succeeds, a live server owns the socket, so fzf refuses to start instead of stealing it. A stale socket file (no listener) is silently removed and reused instead.","triggerScenarios":"Running fzf with --listen /path/to/fzf.sock while a previous fzf instance started with the same --listen address is still alive; e.g. a second fzf in another shell, or a backgrounded fzf server that was never killed. Only triggered when the address string ends in .sock (parseListenAddress routes *.sock to the UNIX branch).","commonSituations":"Editor plugins or shell wrappers that spawn fzf with a fixed socket path and fail to clean up; orphaned fzf processes left by a crashed integration script; two terminal sessions sharing a hardcoded --listen sock path; a hung fzf from a previous session still attached to the socket.","solutions":["Find and stop the process holding the socket: `lsof /path/to/fzf.sock` or `fuser /path/to/fzf.sock`, then kill it, and restart fzf.","If the old instance is intentionally kept, start the new fzf with a different --listen socket path (or omit --listen).","As a last resort, delete the socket file only after confirming nothing is listening: `ss -x | grep fzf.sock` shows no peer, then `rm /path/to/fzf.sock` (note fzf already removes truly dead sockets itself, so manual rm is rarely needed).","Audit the wrapper/plugin that launches fzf to guarantee it terminates the previous instance or uses unique socket paths (e.g. $$ or mktemp-derived names)."],"exampleFix":"# before\nfzf --listen /tmp/fzf.sock   # second instance while first is alive\n\n# after\n# stop the old instance first\nkill $(lsof -t /tmp/fzf.sock) 2>/dev/null\nfzf --listen /tmp/fzf.sock\n\n# or use a per-session socket in a wrapper\nfzf --listen \"/tmp/fzf-$$.sock\"","handlingStrategy":"validation","validationCode":"// Go caller: before launching fzf with --listen sock, probe the socket\nimport (\n    \"net\"\n    \"os/exec\"\n)\n\nfunc socketFree(sockPath string) bool {\n    conn, err := net.Dial(\"unix\", sockPath)\n    if err != nil {\n        return true // stale or absent socket: fzf will remove/reuse it\n    }\n    conn.Close()\n    return false // a live server owns it: fzf will error\n}\n\n// usage\nif !socketFree(\"/tmp/fzf.sock\") {\n    // kill old instance or pick another path before exec\n}\n_ = exec.Command(\"fzf\", \"--listen\", \"/tmp/fzf.sock\")","typeGuard":"// Go: detect the 'socket already in use' error from fzf's stderr\nfunc isSocketInUse(stderr string, sockPath string) bool {\n    return strings.Contains(stderr, \"socket already in use: \"+sockPath)\n}","tryCatchPattern":"// fzf is a CLI; treat a non-zero exit as the 'catch' and branch on stderr\nout, err := cmd.CombinedOutput()\nif err != nil {\n    if strings.Contains(string(out), \"socket already in use\") {\n        // old instance alive: reuse it or kill it, then retry once\n    }\n}","preventionTips":["Generate unique socket paths per session (PID/mktemp) in wrappers that spawn fzf --listen.","Have integrations trap EXIT and kill the fzf server they started.","Before launching, probe the socket with a unix dial and clean up or reuse a live server."],"tags":["go","unix-socket","fzf","server","concurrency"],"backgroundTag":null,"analyzedSha":"bd4efa277b49ef34ca4025bff9b1a288e1980eff","analyzedAt":"2026-08-15T06:11:46.983Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}