{"record":{"id":"1253218e55df5bfd","repo":"larksuite/cli","slug":"file-has-multiple-hard-links-so-the-other-names-i-125321","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_windows.go","lineNumber":57,"sourceCode":"// inspectOpenedFile validates the opened handle. pre is nil when there is no\n// prior Stat to compare against.\nfunc inspectOpenedFile(f *os.File, pre os.FileInfo) 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\tvar handleInfo syscall.ByHandleFileInformation\n\tif err := syscall.GetFileInformationByHandle(syscall.Handle(f.Fd()), &handleInfo); err != nil {\n\t\treturn fmt.Errorf(\"cannot inspect opened file links: %w\", err)\n\t}\n\tif handleInfo.NumberOfLinks > 1 {\n\t\treturn fmt.Errorf(\"file has multiple hard links, so the other names it can be reached by \" +\n\t\t\t\"cannot be checked (hint: copy the file and use the copy instead)\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":39,"sourceCodeEnd":62,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/vfs/localfileio/openvalidated_windows.go#L39-L62","documentation":"The opened file has more than one hard link (NumberOfLinks > 1), meaning it can be reached by multiple directory names. Since the library cannot verify that content written through this handle is not also visible/mutated via the other names, it refuses the file and suggests using a copy. This preserves the write-isolation guarantee of validated opens.","triggerScenarios":"Calling openValidated on Windows on a file that has been hard-linked (e.g. via mklink /H, backup tools' hardlink dedup, package managers like pnpm-style stores) so ByHandleFileInformation.NumberOfLinks > 1.","commonSituations":"Files deduplicated by backup software (Carbonite, Macrium), files hard-linked by cargo/pnpm-style caches, users hard-linking config files across workspaces, files copied with hardlink-preserving tools (robocopy /HK).","solutions":["Copy the file to a new path (a fresh copy has one link) and use the copy, as the error hint suggests","Break the extra links: delete the other directory entries pointing at the same file, leaving one","Replace the hard link with a real copy: copy to a temp file and rename it over the original name","Avoid hardlink-preserving copy flags (e.g. robocopy /J /HK style options) when staging files for this library"],"exampleFix":"// before: hard-linked file\n// mklink /H config-copy.json config.json  -> NumberOfLinks = 2\nf, err := openValidated(\"config-copy.json\", pre)\n// after: use an independent copy\n// copy config-copy.json config-copy.json.tmp && move /y config-copy.json.tmp config-copy.json\nf, err := openValidated(\"config-copy.json\", pre)","handlingStrategy":"validation","validationCode":"// count links via the name before opening (Windows)\nfunc hardLinkCount(path string) (uint32, error) {\n    info, err := os.Stat(path)\n    if err != nil { return 0, err }\n    if !info.Mode().IsRegular() { return 0, fmt.Errorf(\"not a regular file\") }\n    return info.Sys().(*syscall.Win32FileAttributeData).NFileIndexHigh, nil // pair with link-count check via ByHandleFileInformation\n}","typeGuard":null,"tryCatchPattern":"f, err := openValidated(path, pre)\nif err != nil && strings.Contains(err.Error(), \"multiple hard links\") {\n    tmp := path + \".copy.tmp\"\n    if cerr := copyFile(path, tmp); cerr != nil { return cerr }\n    if rerr := os.Rename(tmp, path); rerr != nil { return rerr } // rename replaces links with a single-link file\n    f, err = openValidated(path, nil)\n}\nreturn err","preventionTips":["Avoid hardlink-preserving copies (robocopy /HK, cp -l equivalents) for files fed to this library","Find and remove extra links with fsutil hardlink list <file> before opening","Copy-then-replace (fresh copy has exactly one link) when in doubt"],"tags":["windows","filesystem","hard-links","write-safety"],"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"}