larksuite/cli · error
file has multiple hard links, so the other names it can be r
Error message
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)
What it means
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.
Source
Thrown at internal/vfs/localfileio/openvalidated_windows.go:57
// inspectOpenedFile validates the opened handle. pre is nil when there is no
// prior Stat to compare against.
func inspectOpenedFile(f *os.File, pre os.FileInfo) error {
post, err := f.Stat()
if err != nil {
return fmt.Errorf("cannot stat opened file: %w", err)
}
if pre != nil && !os.SameFile(pre, post) {
return fmt.Errorf("file changed between validation and open")
}
if !post.Mode().IsRegular() {
return fmt.Errorf("not a regular file (directories, devices, FIFOs, and sockets are refused)")
}
var handleInfo syscall.ByHandleFileInformation
if err := syscall.GetFileInformationByHandle(syscall.Handle(f.Fd()), &handleInfo); err != nil {
return fmt.Errorf("cannot inspect opened file links: %w", err)
}
if handleInfo.NumberOfLinks > 1 {
return fmt.Errorf("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)")
}
return nil
}
View on GitHub (pinned to 7fd6ef3c07)
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
Example fix
// before: hard-linked file
// mklink /H config-copy.json config.json -> NumberOfLinks = 2
f, err := openValidated("config-copy.json", pre)
// after: use an independent copy
// copy config-copy.json config-copy.json.tmp && move /y config-copy.json.tmp config-copy.json
f, err := openValidated("config-copy.json", pre) Defensive patterns
Strategy: validation
Validate before calling
// count links via the name before opening (Windows)
func hardLinkCount(path string) (uint32, error) {
info, err := os.Stat(path)
if err != nil { return 0, err }
if !info.Mode().IsRegular() { return 0, fmt.Errorf("not a regular file") }
return info.Sys().(*syscall.Win32FileAttributeData).NFileIndexHigh, nil // pair with link-count check via ByHandleFileInformation
} Try / catch
f, err := openValidated(path, pre)
if err != nil && strings.Contains(err.Error(), "multiple hard links") {
tmp := path + ".copy.tmp"
if cerr := copyFile(path, tmp); cerr != nil { return cerr }
if rerr := os.Rename(tmp, path); rerr != nil { return rerr } // rename replaces links with a single-link file
f, err = openValidated(path, nil)
}
return err Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- cannot inspect opened file links: %w
- %s: cannot stat %q: %w
- cannot stat opened file: %w
- file changed between validation and open
- not a regular file (directories, devices, FIFOs, and sockets
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/1253218e55df5bfd.
Report an issue: GitHub.