docker/cli · critical

manifest verification failed for digest

Error message

manifest verification failed for digest %s

What it means

validateManifestDigest recomputes the manifest digest from its canonical payload. When you pull by digest (@sha256:...) and the computed digest differs from the one you requested, the manifest does not match — a sign of corruption, tampering, or a digest mismatch. Pulls by tag skip this check.

Solutions

  1. Double-check the requested digest string for typos (length, algorithm prefix).
  2. Re-pull from a trusted mirror to rule out in-flight corruption.
  3. Re-push the image to refresh manifest and digest.
  4. Inspect for a MITM/proxy modifying payloads; pin to content-trusted registries.

Example fix

// before
ref, _ := reference.WithDigest(name, badDigest)
c.GetManifest(ctx, ref) // -> manifest verification failed

// after
ref, _ := reference.WithDigest(name, correctDigest) // copy digest exactly
c.GetManifest(ctx, ref)
Defensive patterns

Strategy: validation

Validate before calling

// Recompute and compare the manifest digest client-side before trusting it.
func checkDigest(mfst distribution.Manifest, want digest.Digest) error {
    _, canon, err := mfst.Payload()
    if err != nil {
        return err
    }
    if got := digest.FromBytes(canon); got != want {
        return fmt.Errorf("digest mismatch: got %s want %s", got, want)
    }
    return nil
}

Prevention

When it happens

Trigger: Pulling a manifest by digest where the registry-returned manifest's recomputed digest does not equal the requested digest — wrong content served, transit corruption, or a hand-typed wrong digest.

Common situations: Digest copied incorrectly; registry serving stale/wrong content for the digest; content mutated by a proxy; manifest re-signed or repackaged under the same digest by mistake.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/0884a491546733e8. Report an issue: GitHub.

Appendix: source

Thrown at internal/registryclient/fetcher.go:147

	return configJSON, nil
}

// validateManifestDigest computes the manifest digest, and, if pulling by
// digest, ensures that it matches the requested digest.
func validateManifestDigest(ref reference.Named, mfst distribution.Manifest) (ocispec.Descriptor, error) {
	mediaType, canonical, err := mfst.Payload()
	if err != nil {
		return ocispec.Descriptor{}, err
	}
	desc := ocispec.Descriptor{
		Digest:    digest.FromBytes(canonical),
		Size:      int64(len(canonical)),
		MediaType: mediaType,
	}

	// If pull by digest, then verify the manifest digest.
	if digested, isDigested := ref.(reference.Canonical); isDigested && digested.Digest() != desc.Digest {
		return ocispec.Descriptor{}, fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
	}

	return desc, nil
}

// pullManifestList handles "manifest lists" which point to various
// platform-specific manifests.
func pullManifestList(ctx context.Context, ref reference.Named, repo distribution.Repository, mfstList manifestlist.DeserializedManifestList) ([]types.ImageManifest, error) {
	if _, err := validateManifestDigest(ref, mfstList); err != nil {
		return nil, err
	}

	infos := make([]types.ImageManifest, 0, len(mfstList.Manifests))
	for _, manifestDescriptor := range mfstList.Manifests {
		manSvc, err := repo.Manifests(ctx)
		if err != nil {
			return nil, err
		}

View on GitHub (pinned to 4f84911bfe)