{"record":{"id":"8146b1b8466705f9","repo":"larksuite/cli","slug":"file-has-multiple-hard-links-so-the-other-names-i","errorCode":null,"errorMessage":"file has multiple hard links, so the other names it can be reached by cannot be checked (hint: copy the file and use the copy instead)","messagePattern":"file has multiple hard links, so the other names it can be reached by cannot be checked \\(hint: copy the file and use the copy instead\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/vfs/localfileio/openvalidated_unix.go","lineNumber":71,"sourceCode":"\treturn f, nil\n}\n\n// inspectOpenedFile validates the opened fd and restores blocking mode. pre is\n// nil when there is no prior Stat to compare against.\nfunc inspectOpenedFile(f *os.File, pre os.FileInfo, rejectHardLinks bool) error {\n\tpost, err := f.Stat()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot stat opened file: %w\", err)\n\t}\n\tif pre != nil && !os.SameFile(pre, post) {\n\t\treturn fmt.Errorf(\"file changed between validation and open\")\n\t}\n\tif !post.Mode().IsRegular() {\n\t\treturn fmt.Errorf(\"not a regular file (directories, devices, FIFOs, and sockets are refused)\")\n\t}\n\tif rejectHardLinks {\n\t\tif st, ok := post.Sys().(*syscall.Stat_t); ok && st.Nlink > 1 {\n\t\t\treturn fmt.Errorf(\"file has multiple hard links, so the other names it can be reached by \" +\n\t\t\t\t\"cannot be checked (hint: copy the file and use the copy instead)\")\n\t\t}\n\t}\n\tif err := syscall.SetNonblock(int(f.Fd()), false); err != nil {\n\t\treturn fmt.Errorf(\"cannot restore blocking mode: %w\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":53,"sourceCodeEnd":80,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/vfs/localfileio/openvalidated_unix.go#L53-L80","documentation":"The opened file's st.Nlink > 1 and hard-link rejection is enabled (openValidated always passes true). An extra name lets a denylisted file be smuggled into an allowed directory, and the filesystem cannot enumerate the other names, so the library bluntly refuses any multi-linked file — including benign layouts like pnpm/nix content-addressed stores.","triggerScenarios":"Any openValidated read of a file whose inode has 2+ directory entries: hard links created with ln, or content-addressed package stores (pnpm, nix, cargo hardlink caches) linking into a shared store outside the allowlist.","commonSituations":"Reading a file inside a pnpm node_modules or nix store path; a hardlink-based dedup/backup tool (rsync --link-dest) touched the file; user created a hard link for convenience.","solutions":["Copy the file (cp, not ln) and use the copy — this is the documented workaround","If the file lives in a pnpm/nix-style store, resolve to the real file and copy it out, or reinstall with a non-hardlink layout (e.g. pnpm with hoisted node-linker)","Check with stat: if Links is 1 the error was stale/re-raced; retry","Avoid hardlink-based dedup tools on directories the CLI must read"],"exampleFix":"// before\nln config.yaml /workdir/config.yaml   # Nlink=2, refused\n// after\ncp config.yaml /workdir/config.yaml   # independent inode, accepted","handlingStrategy":"validation","validationCode":"info, err := os.Stat(path)\nif err != nil { return err }\nif st, ok := info.Sys().(*syscall.Stat_t); ok && st.Nlink > 1 {\n    // copy to a fresh file before use\n    tmp := path + \".copy\"\n    if err := copyFile(path, tmp); err != nil { return err }\n    path = tmp\n}","typeGuard":null,"tryCatchPattern":"var pve *fileio.PathValidationError\nif errors.As(err, &pve) && strings.Contains(pve.Error(), \"multiple hard links\") { /* copy the file and retry with the copy */ }","preventionTips":["Use cp, never ln, when staging files the CLI reads","Stat the file first and refuse Nlink>1 (unix: info.Sys().(*syscall.Stat_t).Nlink)","Avoid reading directly from pnpm/nix content-addressed stores; copy out first","Skip hardlink-dedup (rsync --link-dest) in directories the CLI targets"],"tags":["filesystem","hardlink","security","unix"],"backgroundTag":"multiple-hard-links","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}