k3s-io/k3s · error
failed %d link verifications
Error message
failed %d link verifications
What it means
VerifyLinks calls os.Readlink on each file listed in .links and compares the target against the expected value (mismatches logged as 'Link for file %s expected to be %s (fail)'). A non-zero mismatch count returns this error - required symlinks are missing (Readlink error counts as mismatch) or point at the wrong target.
Source
Thrown at pkg/dataverify/dataverify.go:80
if err != nil {
return err
}
if len(links) == 0 {
return fmt.Errorf("no entries found in %s", linkListFile)
}
numFailed := 0
for linkFile, linkExpected := range links {
file := filepath.Join(root, linkFile)
linkActual, _ := os.Readlink(file)
if linkExpected != linkActual {
logrus.Errorf("Link for file %s expected to be %s (fail)", linkFile, linkExpected)
numFailed++
} else {
logrus.Debugf("Verified link %s is correct", linkFile)
}
}
if numFailed != 0 {
return fmt.Errorf("failed %d link verifications", numFailed)
}
return nil
}
func fileMapFields(fileName string, key, val int) (map[string]string, error) {
file, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer file.Close()
result := map[string]string{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) == 0 {
continue
}
if len(fields) <= key || len(fields) <= val {View on GitHub (pinned to 6ba341e396)
Solutions
- Check the per-file 'Link for file ... (fail)' logs, then ls -l the named paths to see what they actually point to.
- Delete the temp data dir and let k3s re-extract natively on a POSIX filesystem (ext4/xfs) - do not pre-copy the data dir with tools that dereference symlinks.
- If a custom data-dir location is on a filesystem that cannot store symlinks, move it to one that can.
Example fix
# before (breaks symlinks) scp -r /opt/k3s-data/ node:/var/lib/rancher/k3s/data/ # or tar with --dereference # after (preserve symlinks) tar -C /opt -cf - k3s-data | ssh node 'tar -C /var/lib/rancher/k3s/data/ -xf -'
Defensive patterns
Strategy: validation
Validate before calling
// Verify required symlinks survive staging:
links := parseLinks(filepath.Join(dir, ".links")) // map[link]target
for link, want := range links {
got, err := os.Readlink(filepath.Join(dir, link))
if err != nil || got != want {
log.Fatalf("symlink %s: got %q err=%v want %q", link, got, err, want)
}
} Prevention
- Transfer data dirs only with symlink-preserving tools (tar over ssh, rsync -a).
- Use ext4/xfs for the data dir; FAT and some CIFS modes cannot store symlinks.
- Do not 'fix' broken-looking symlinks by replacing them with copies.
When it happens
Trigger: A file listed in .links is not a symlink, or its target differs from the recorded value, inside <data-dir>/data/<version>-tmp/bin (pkg/dataverify/dataverify.go:67-80).
Common situations: Extraction performed by a tool that flattened symlinks into copies (some Windows/SCP/NFS transfers); someone replaced a symlink with a real file; filesystem types (FAT) or containers not preserving symlinks; hand-restored data dir from tar without -h/--dereference awareness.
Related errors
- failed to verify directory %s
- no entries found in %s
- failed %d hash verifications
- tar error: %v
- fields for file %s (%d) smaller than required index (key: %d
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/b16bea9516ddcb91.
Report an issue: GitHub.