twpayne/chezmoi · error
size mismatch: expected %d, got %d
Error message
size mismatch: expected %d, got %d
What it means
When downloading an external (archive/file), chezmoi verifies the downloaded data length against the declared checksum.size from the external definition. If the byte count differs, this error is appended and the external is rejected, because a size mismatch may indicate a corrupted or tampered download.
Source
Thrown at internal/chezmoi/sourcestate.go:1689
func (s *SourceState) getExternalData(
ctx context.Context,
externalRelPath RelPath,
external *External,
options *ReadOptions,
) ([]byte, string, error) {
data, urlStr, err := s.getExternalDataAndURL(ctx, externalRelPath, external, options)
if err != nil {
return nil, "", err
}
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",View on GitHub (pinned to f901167e46)
Solutions
- Re-verify the download URL and update the size (and hashes) to match the current release artifact
- Pin the URL to an exact versioned release instead of a 'latest' endpoint
- Re-download the artifact yourself, confirm its byte size, and update the external definition
Example fix
# before [".local/bin/tool"] type = "file" url = "https://example.com/tool-latest" size = 1234567 # after [".local/bin/tool"] type = "file" url = "https://example.com/tool-1.2.3" size = 1234890 sha256 = "<hash-of-tool-1.2.3>"
Defensive patterns
Strategy: validation
Validate before calling
resp, _ := http.Get(url)
n, _ := io.Copy(io.Discard, resp.Body)
if int(n) != declaredSize {
fmt.Printf("upstream artifact size %d != declared %d; update external definition\n", n, declaredSize)
} Prevention
- Pin externals to exact release URLs, never 'latest'
- Record sha256 alongside size for every external
- Re-checksum artifacts when the upstream release changes
When it happens
Trigger: len(data) != external.Checksum.Size while verifying a downloaded external in TestFS/NewTar/tarAddEntry paths (sourcestate.go:1689) — the fetched content's byte count differs from the declared size in .chezmoiexternal.
Common situations: The upstream project released a new version at the same URL (moving latest), the URL was mistyped, a proxy/mirror truncated the download, or the recorded size was copied from a different release.
Related errors
- MD5 mismatch: expected %s, got %s
- 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
AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01).
Data as JSON: /api/errors/13b8880dc40a73ae.
Report an issue: GitHub.