hashicorp/nomad · error
failed to parse hash file %s
Error message
failed to parse hash file %s
What it means
This error is returned by ResolvConf.UserModified when the contents of the hash file cannot be parsed as a valid digest via digest.Parse. The hash file is expected to contain a valid digest string; if it is empty, truncated, or contains garbage, the stored expected hash cannot be reconstructed and the comparison against the live resolv.conf cannot proceed.
Source
Thrown at lib/resolvconf/lib.go:420
}
// UserModified can be used to determine whether the resolv.conf file has been
// modified since it was generated. It returns false with no error if the file
// matches the hash, true with no error if the file no longer matches the hash,
// and false with an error if the result cannot be determined.
func UserModified(rcPath, rcHashPath string) (bool, error) {
currRCHash, err := os.ReadFile(rcHashPath)
if err != nil {
// 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] == ';' {View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect the hash file contents (cat rcHashPath) — if empty or corrupt, delete it; the next Set/user flow will rewrite it and UserModified will report unmodified until then.
- Ensure the code that writes the hash file writes it atomically (write to temp file then rename) to avoid truncated contents.
- Check for concurrent writers to the same hash path (multiple processes/containers sharing the state dir) and isolate the path per instance.
- Upgrade/align library versions so the digest format written matches the format parsed.
Example fix
// before: corrupt/truncated hash file // cat /var/run/resolvconf.hash -> "" (empty) // UserModified() -> failed to parse hash file ... // after // rm /var/run/resolvconf.hash // regenerate via library Set // UserModified() -> (false, nil)
Defensive patterns
Strategy: validation
Validate before calling
data, err := os.ReadFile(rcHashPath)
if err != nil { return err }
if len(data) == 0 {
// invalid digest: delete file so library regenerates it
os.Remove(rcHashPath)
} Type guard
func hashFileNonEmpty(path string) bool {
fi, err := os.Stat(path)
return err == nil && fi.Mode().IsRegular() && fi.Size() > 0
} Try / catch
modified, err := rc.UserModified()
if err != nil && strings.Contains(err.Error(), "failed to parse hash file") {
os.Remove(rcHashPath) // regenerate on next write
modified = false
} Prevention
- Write the hash file atomically (temp file + rename) to avoid truncated contents.
- Never hand-edit or redirect other output into the hash file.
- Avoid multiple processes sharing the same hash path.
- Pin library versions so digest formats stay compatible.
When it happens
Trigger: Calling ResolvConf.UserModified when the hash file at rcHashPath exists and is readable but its contents are not a valid digest — e.g. an empty file, a partially written file from an interrupted Set, or a file overwritten by another tool.
Common situations: Crash or SIGKILL between file creation and full write of the hash; an init script or backup tool clobbering the hash file; manual editing of the state file; different library versions writing incompatible digest formats.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- users: unable to parse uid/gid from username
- <combined HCL diagnostics from str.String()>
- detected corrupted token within the state store: missing rol
- Failed to parse ACL Policy: %v
- Failed to parse X-Nomad-LastContact: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/e7f37d85b5257eb4.
Report an issue: GitHub.