twpayne/chezmoi · error
MD5 mismatch: expected %s, got %s
Error message
MD5 mismatch: expected %s, got %s
What it means
chezmoi downloads external file data and verifies it against checksums declared in the source state's external entry. When the MD5 checksum configured in .chezmoiexternal does not match the MD5 of the downloaded bytes, the computed and expected digests are formatted into this error and accumulated. MD5 is deprecated for security, so chezmoi also warns that support will be removed; use SHA256 instead.
Source
Thrown at internal/chezmoi/sourcestate.go:1700
var errs []error
if external.Checksum.Size != 0 {
if external.Checksum.SHA256 == nil && external.Checksum.SHA384 == nil && external.Checksum.SHA512 == nil {
s.warnFunc("%s: warning: insecure size check without secure hash will be removed\n", externalRelPath)
}
if len(data) != external.Checksum.Size {
err := fmt.Errorf("size mismatch: expected %d, got %d", external.Checksum.Size, len(data))
errs = append(errs, err)
}
}
if external.Checksum.MD5 != nil {
s.warnFunc(
"%s: warning: insecure MD5 checksum will be removed, use a secure hash like SHA256 instead\n",
externalRelPath,
)
if gotMD5Sum := md5Sum(data); !bytes.Equal(gotMD5Sum, external.Checksum.MD5) {
err := fmt.Errorf("MD5 mismatch: expected %s, got %s", external.Checksum.MD5, hex.EncodeToString(gotMD5Sum))
errs = append(errs, err)
}
}
if external.Checksum.RIPEMD160 != nil {
s.warnFunc(
"%s: warning: insecure RIPEMD-160 checksum will be removed, use a secure hash like SHA256 instead\n",
externalRelPath,
)
if gotRIPEMD160Sum := ripemd160Sum(data); !bytes.Equal(gotRIPEMD160Sum, external.Checksum.RIPEMD160) {
format := "RIPEMD-160 mismatch: expected %s, got %s"
err := fmt.Errorf(format, external.Checksum.RIPEMD160, hex.EncodeToString(gotRIPEMD160Sum))
errs = append(errs, err)
}
}
if external.Checksum.SHA1 != nil {
s.warnFunc(View on GitHub (pinned to f901167e46)
Solutions
- Recompute the correct checksum: curl the URL and run md5sum, then update checksum.md5 in .chezmoiexternal.
- Prefer a secure hash: compute sha256sum and declare checksum.sha256 instead (MD5 support is being removed).
- If upstream is genuinely volatile, drop the checksum field entirely (losing integrity verification).
- Check the download path (proxy/mirror) is serving the exact bytes you hashed.
Example fix
// before (.chezmoiexternal.toml) [.tmux.conf] type = "file" url = "https://example.com/tmux.conf" checksum.md5 = "abcd1234" // after # md5sum tmux.conf -> e3b0c44298fc1c149afbf4c8996fb924 checksum.sha256 = "<sha256 of file>"
Defensive patterns
Strategy: validation
Validate before calling
# Verify the checksum before chezmoi does: curl -sL "https://example.com/artifact" | md5sum # Compare with checksum.md5 in .chezmoiexternal; prefer: curl -sL "https://example.com/artifact" | sha256sum
Try / catch
// Run `chezmoi apply --dry-run` (or `chezmoi diff`) and treat any output containing
// 'MD5 mismatch' as a hard stop; fix .chezmoiexternal before applying.
if err := run("chezmoi", "apply", "--dry-run"); strings.Contains(err.Error(), "mismatch") {
return fmt.Errorf("external checksum drift: %w", err)
} Prevention
- Always pin checksum.sha256, not MD5 (MD5 is insecure and slated for removal).
- Generate checksums in CI directly from the release artifact, never by hand.
- Pin URLs to immutable release assets, not moving 'latest' links.
- Run chezmoi apply --dry-run in CI to catch checksum drift early.
When it happens
Trigger: An external entry in .chezmoiexternal with a checksum.md5 field is fetched; md5Sum(data) differs from external.Checksum.MD5. Happens when the upstream file changed since the checksum was recorded, or the checksum was copy-pasted/truncated incorrectly.
Common situations: Pinning a GitHub release tarball by MD5 that upstream re-released; hand-computing the checksum on a different file; proxy or mirror serving altered content; declaring an MD5 checksum against a file that is actually SHA256-encoded hex.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- RIPEMD-160 mismatch: expected %s, got %s
- SHA1 mismatch: expected %s, got %s
- SHA256 mismatch: expected %s, got %s
- SHA384 mismatch: expected %s, got %s
- SHA512 mismatch: expected %s, got %s
AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01).
Data as JSON: /api/errors/4b7b95d544216d3f.
Report an issue: GitHub.