larksuite/cli · error
file has multiple hard links, so the other names it can be…
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.
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
- cannot stat opened file
- file changed between validation and open
- local input path contains a reserved Windows path component
- local input path must not use a Windows network or device…
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/1253218e55df5bfd.
Report an issue: GitHub.
Appendix: 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)