{"record":{"id":"b16bea9516ddcb91","repo":"k3s-io/k3s","slug":"failed-d-link-verifications","errorCode":null,"errorMessage":"failed %d link verifications","messagePattern":"failed (.+?) link verifications","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/dataverify/dataverify.go","lineNumber":80,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tif len(links) == 0 {\n\t\treturn fmt.Errorf(\"no entries found in %s\", linkListFile)\n\t}\n\tnumFailed := 0\n\tfor linkFile, linkExpected := range links {\n\t\tfile := filepath.Join(root, linkFile)\n\t\tlinkActual, _ := os.Readlink(file)\n\t\tif linkExpected != linkActual {\n\t\t\tlogrus.Errorf(\"Link for file %s expected to be %s (fail)\", linkFile, linkExpected)\n\t\t\tnumFailed++\n\t\t} else {\n\t\t\tlogrus.Debugf(\"Verified link %s is correct\", linkFile)\n\t\t}\n\t}\n\tif numFailed != 0 {\n\t\treturn fmt.Errorf(\"failed %d link verifications\", numFailed)\n\t}\n\treturn nil\n}\n\nfunc fileMapFields(fileName string, key, val int) (map[string]string, error) {\n\tfile, err := os.Open(fileName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\tresult := map[string]string{}\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tfields := strings.Fields(scanner.Text())\n\t\tif len(fields) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif len(fields) <= key || len(fields) <= val {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/dataverify/dataverify.go#L62-L98","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before (breaks symlinks)\nscp -r /opt/k3s-data/ node:/var/lib/rancher/k3s/data/   # or tar with --dereference\n\n# after (preserve symlinks)\ntar -C /opt -cf - k3s-data | ssh node 'tar -C /var/lib/rancher/k3s/data/ -xf -'","handlingStrategy":"validation","validationCode":"// Verify required symlinks survive staging:\nlinks := parseLinks(filepath.Join(dir, \".links\")) // map[link]target\nfor link, want := range links {\n    got, err := os.Readlink(filepath.Join(dir, link))\n    if err != nil || got != want {\n        log.Fatalf(\"symlink %s: got %q err=%v want %q\", link, got, err, want)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["integrity","symlinks","deployment","filesystem"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}