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

  1. Re-verify the download URL and update the size (and hashes) to match the current release artifact
  2. Pin the URL to an exact versioned release instead of a 'latest' endpoint
  3. 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

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


AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01). Data as JSON: /api/errors/13b8880dc40a73ae. Report an issue: GitHub.