hashicorp/nomad · error
failed to hash %s to check for modifications
Error message
failed to hash %s to check for modifications
What it means
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.
Source
Thrown at lib/resolvconf/lib.go:429
// If the hash file doesn't exist, can only assume it hasn't been written
// yet (so, the user hasn't modified the file it hashes).
if errors.Is(err, fs.ErrNotExist) {
return false, nil
}
return false, errors.Wrapf(err, "failed to read hash file %s", rcHashPath)
}
expected, err := digest.Parse(string(currRCHash))
if err != nil {
return false, errors.Wrapf(err, "failed to parse hash file %s", rcHashPath)
}
v := expected.Verifier()
currRC, err := os.Open(rcPath)
if err != nil {
return false, errors.Wrapf(err, "failed to open %s to check for modifications", rcPath)
}
defer currRC.Close()
if _, err := io.Copy(v, currRC); err != nil {
return false, errors.Wrapf(err, "failed to hash %s to check for modifications", rcPath)
}
return !v.Verified(), nil
}
func (rc *ResolvConf) processLine(line string) {
fields := strings.Fields(line)
// Strip blank lines and comments.
if len(fields) == 0 || fields[0][0] == '#' || fields[0][0] == ';' {
return
}
switch fields[0] {
case "nameserver":
if len(fields) < 2 {
return
}
if addr, err := netip.ParseAddr(fields[1]); err != nil {View on GitHub (pinned to 482b49bf1a)
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.
Example fix
// before: rcPath replaced mid-read
// UserModified() -> failed to hash ... to check for modifications
// after: retry with backoff
// var modified bool
// for i := 0; i < 3; i++ {
// modified, err = rc.UserModified()
// if err == nil { break }
// time.Sleep(100 * time.Millisecond)
// } Defensive patterns
Strategy: retry
Validate before calling
if f, err := os.Open(rcPath); err != nil {
// cannot open; don't bother calling UserModified
} else {
f.Close()
} Type guard
func canReadFully(path string) bool {
f, err := os.Open(path)
if err != nil { return false }
defer f.Close()
_, err = io.Copy(io.Discard, f)
return err == nil
} Try / catch
var modified bool
var lastErr error
for i := 0; i < 3; i++ {
modified, lastErr = rc.UserModified()
if lastErr == nil || !strings.Contains(lastErr.Error(), "failed to hash") {
break
}
time.Sleep(100 * time.Millisecond << i)
}
return modified, lastErr Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to read hash file %s
- failed to open %s to check for modifications
- unable to read rooted allocation directory
- can't seek to offset %d: %w
- Couldn't copy %q to %q: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/e4415beb814863b0.
Report an issue: GitHub.