docker/compose · critical
blob digest mismatch: expected %s, got %s
Error message
blob digest mismatch: expected %s, got %s
What it means
The strict integrity check: GetBlob recomputes the digest of the downloaded bytes with the descriptor's algorithm and compares it to descriptor.Digest. A mismatch means the content served does not match what the manifest promises — corrupt storage, a compromised/rogue registry, a MITM tampering the stream, or a descriptor built against different bytes. The bytes are rejected and never written to disk by callers.
Source
Thrown at internal/oci/resolver.go:128
return nil, fmt.Errorf("fetching blob %s: %w", descriptor.Digest, err)
}
defer func() { _ = fetch.Close() }()
// bound the read by the declared size so a rogue registry can't cause
// unbounded allocation; the extra byte detects oversized responses.
content, err := io.ReadAll(io.LimitReader(fetch, descriptor.Size+1))
if err != nil {
return nil, fmt.Errorf("reading blob %s: %w", descriptor.Digest, err)
}
if int64(len(content)) != descriptor.Size {
return nil, fmt.Errorf("blob %s size mismatch: expected %d bytes, got %d", descriptor.Digest, descriptor.Size, len(content))
}
// GetBlob bypasses containerd's content store, so integrity must be
// checked here before callers write the bytes to disk.
if err := descriptor.Digest.Validate(); err != nil {
return nil, fmt.Errorf("invalid digest %s: %w", descriptor.Digest, err)
}
if actual := descriptor.Digest.Algorithm().FromBytes(content); actual != descriptor.Digest {
return nil, fmt.Errorf("blob digest mismatch: expected %s, got %s", descriptor.Digest, actual)
}
return content, nil
}
func Copy(ctx context.Context, resolver remotes.Resolver, image reference.Named, named reference.Named) (spec.Descriptor, error) {
src, desc, err := resolver.Resolve(ctx, image.String())
if err != nil {
return spec.Descriptor{}, err
}
if desc.Annotations == nil {
desc.Annotations = make(map[string]string)
}
// set LabelDistributionSource so push will actually use a registry mount
refspec := reference.TrimNamed(image).String()
u, err := url.Parse("dummy://" + refspec)
if err != nil {
return spec.Descriptor{}, err
}View on GitHub (pinned to ddc4b044b6)
Solutions
- Re-pull from a trusted network path without TLS-intercepting proxies.
- Re-push the artifact from a known-good source so manifest and blobs agree.
- Run registry storage checks (fsck/repair) if self-hosting.
- Treat repeated mismatches from one registry as a security incident: verify the artifact from another mirror.
Defensive patterns
Strategy: validation
Validate before calling
// verify before trusting anything the registry served
actual := descriptor.Digest.Algorithm().FromBytes(content)
if actual != descriptor.Digest {
return fmt.Errorf("content integrity failure for %s: got %s", descriptor.Digest, actual)
} Type guard
func blobMatchesDigest(content []byte, expected digest.Digest) bool {
return expected.Algorithm().FromBytes(content) == expected
} Try / catch
if actual := descriptor.Digest.Algorithm().FromBytes(content); actual != descriptor.Digest {
return nil, fmt.Errorf("blob digest mismatch: expected %s, got %s", descriptor.Digest, actual)
// discard bytes; do not retry from same source; treat as tampering/corruption
} Prevention
- Never skip content verification for blobs fetched outside containerd's content store.
- Re-push from a trusted source on mismatch; investigate the registry if it repeats.
- Keep TLS-intercepting proxies out of the registry path.
When it happens
Trigger: descriptor.Digest.Algorithm().FromBytes(content) != descriptor.Digest after a successful, correctly-sized download: registry bit rot, proxy tampering, manifest referencing a blob rewritten by GC dedup, or a malicious registry.
Common situations: Self-hosted registry with disk corruption; transparent TLS-inspecting proxies rewriting bodies; artifacts built by tooling that computed digests before final byte changes; CDN caching mismatched bodies under the same URL.
Related errors
- blob %s size mismatch: expected %d bytes, got %d
- invalid digest %s: %w
- unsupported OCI version: %s
- creating fetcher for %s: %w
- fetching blob %s: %w
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/16663c50a4654d13.
Report an issue: GitHub.