twpayne/chezmoi · error

SHA384 mismatch: expected

Error message

SHA384 mismatch: expected %s, got %s

What it means

The SHA384 branch of external checksum verification: if external.Checksum.SHA384 is set and sha384Sum(data) differs, the formatted mismatch error is appended to errs. It is later surfaced wrapped by the external's relative path via errors.Join.

Solutions

  1. Recompute with openssl dgst -sha384 <file> (or curl -sL <url> | openssl dgst -sha384) and update checksum.sha384.
  2. Prefer checksum.sha256, the conventionally supported secure option.
  3. Ensure the digest is full-length hex (96 hex chars for SHA384).
  4. Verify the download pipeline (proxy/mirror) isn't altering content.

Example fix

// before
checksum.sha384 = "<old digest>"
// after
checksum.sha384 = "<freshly computed sha384 of the artifact>"
Defensive patterns

Strategy: validation

Validate before calling

openssl dgst -sha384 <file>  # or: curl -sL "$URL" | openssl dgst -sha384
# Must yield 96 hex chars matching checksum.sha384.

Try / catch

out, err := exec.Command("chezmoi", "apply").CombinedOutput()
if err != nil && strings.Contains(string(out), "SHA384 mismatch") {
    return errors.New("recompute checksum.sha384 from the current artifact")
}

Prevention

When it happens

Trigger: External entry declares checksum.sha384; downloaded bytes hash differently than the pinned SHA384 digest.

Common situations: Artifact updated upstream after pinning; checksum generated on a different file or from base64 vs hex confusion; truncated digest in the config.

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


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

Appendix: source

Thrown at internal/chezmoi/sourcestate.go:1738

			externalRelPath,
		)
		if gotSHA1Sum := sha1Sum(data); !bytes.Equal(gotSHA1Sum, external.Checksum.SHA1) {
			err := fmt.Errorf("SHA1 mismatch: expected %s, got %s", external.Checksum.SHA1, hex.EncodeToString(gotSHA1Sum))
			errs = append(errs, err)
		}
	}

	if external.Checksum.SHA256 != nil {
		if gotSHA256Sum := sha256.Sum256(data); !bytes.Equal(gotSHA256Sum[:], external.Checksum.SHA256) {
			format := "SHA256 mismatch: expected %s, got %s"
			err := fmt.Errorf(format, external.Checksum.SHA256, hex.EncodeToString(gotSHA256Sum[:]))
			errs = append(errs, err)
		}
	}

	if external.Checksum.SHA384 != nil {
		if gotSHA384Sum := sha384Sum(data); !bytes.Equal(gotSHA384Sum, external.Checksum.SHA384) {
			errs = append(errs, fmt.Errorf("SHA384 mismatch: expected %s, got %s",
				external.Checksum.SHA384, hex.EncodeToString(gotSHA384Sum)))
		}
	}

	if external.Checksum.SHA512 != nil {
		if gotSHA512Sum := sha512Sum(data); !bytes.Equal(gotSHA512Sum, external.Checksum.SHA512) {
			errs = append(errs, fmt.Errorf("SHA512 mismatch: expected %s, got %s",
				external.Checksum.SHA512, hex.EncodeToString(gotSHA512Sum)))
		}
	}

	if len(errs) != 0 {
		return nil, urlStr, fmt.Errorf("%s: %w", externalRelPath, errors.Join(errs...))
	}

	if external.Encrypted {
		data, err = s.encryption.Decrypt(data)
		if err != nil {

View on GitHub (pinned to f901167e46)