{"record":{"id":"e4415beb814863b0","repo":"hashicorp/nomad","slug":"failed-to-hash-s-to-check-for-modifications","errorCode":null,"errorMessage":"failed to hash %s to check for modifications","messagePattern":"failed to hash (.+?) to check for modifications","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/resolvconf/lib.go","lineNumber":429,"sourceCode":"\t\t// If the hash file doesn't exist, can only assume it hasn't been written\n\t\t// yet (so, the user hasn't modified the file it hashes).\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, errors.Wrapf(err, \"failed to read hash file %s\", rcHashPath)\n\t}\n\texpected, err := digest.Parse(string(currRCHash))\n\tif err != nil {\n\t\treturn false, errors.Wrapf(err, \"failed to parse hash file %s\", rcHashPath)\n\t}\n\tv := expected.Verifier()\n\tcurrRC, err := os.Open(rcPath)\n\tif err != nil {\n\t\treturn false, errors.Wrapf(err, \"failed to open %s to check for modifications\", rcPath)\n\t}\n\tdefer currRC.Close()\n\tif _, err := io.Copy(v, currRC); err != nil {\n\t\treturn false, errors.Wrapf(err, \"failed to hash %s to check for modifications\", rcPath)\n\t}\n\treturn !v.Verified(), nil\n}\n\nfunc (rc *ResolvConf) processLine(line string) {\n\tfields := strings.Fields(line)\n\n\t// Strip blank lines and comments.\n\tif len(fields) == 0 || fields[0][0] == '#' || fields[0][0] == ';' {\n\t\treturn\n\t}\n\n\tswitch fields[0] {\n\tcase \"nameserver\":\n\t\tif len(fields) < 2 {\n\t\t\treturn\n\t\t}\n\t\tif addr, err := netip.ParseAddr(fields[1]); err != nil {","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/lib/resolvconf/lib.go#L411-L447","documentation":"This error is returned by ResolvConf.UserModified when opening resolv.conf succeeded but streaming its contents into the digest verifier via io.Copy failed. The library computes the live file's hash by copying it into the verifier; an I/O error mid-read means the comparison cannot be completed and the error is wrapped and returned.","triggerScenarios":"Calling ResolvConf.UserModified when reading rcPath fails partway through: disk I/O errors, the file being replaced/truncated concurrently (e.g. resolv.conf rewritten while reading), or a network/overlay filesystem returning EIO.","commonSituations":"Underlying storage failure on the host; container runtime or systemd-resolved replacing the resolv.conf bind-mount mid-read; NFS/overlayfs flakiness; hardware errors visible in dmesg.","solutions":["Check dmesg/journal for I/O errors on the backing storage and fix the disk/filesystem issue (fsck, replace failing hardware).","Retry UserModified — a transient read race with resolv.conf being replaced usually resolves on a second attempt.","Avoid concurrent replacement of rcPath while reading (pause resolvconf/systemd-resolved updates or pin the file).","If on a network/overlay filesystem, move state and resolv.conf to local stable storage."],"exampleFix":"// before: rcPath replaced mid-read\n// UserModified() -> failed to hash ... to check for modifications\n\n// after: retry with backoff\n// var modified bool\n// for i := 0; i < 3; i++ {\n// \tmodified, err = rc.UserModified()\n// \tif err == nil { break }\n// \ttime.Sleep(100 * time.Millisecond)\n// }","handlingStrategy":"retry","validationCode":"if f, err := os.Open(rcPath); err != nil {\n\t// cannot open; don't bother calling UserModified\n} else {\n\tf.Close()\n}","typeGuard":"func canReadFully(path string) bool {\n\tf, err := os.Open(path)\n\tif err != nil { return false }\n\tdefer f.Close()\n\t_, err = io.Copy(io.Discard, f)\n\treturn err == nil\n}","tryCatchPattern":"var modified bool\nvar lastErr error\nfor i := 0; i < 3; i++ {\n\tmodified, lastErr = rc.UserModified()\n\tif lastErr == nil || !strings.Contains(lastErr.Error(), \"failed to hash\") {\n\t\tbreak\n\t}\n\ttime.Sleep(100 * time.Millisecond << i)\n}\nreturn modified, lastErr","preventionTips":["Retry transient read failures with backoff — resolv.conf is often replaced concurrently.","Pause resolvconf/systemd-resolved updates during critical reads.","Check dmesg for disk I/O errors at first occurrence.","Prefer local stable storage over NFS/overlay for resolv.conf and state."],"tags":["resolvconf","io","dns","filesystem"],"backgroundTag":"file-read-io-error","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}