docker/compose · error
blob %s size mismatch: expected %d bytes, got %d
Error message
blob %s size mismatch: expected %d bytes, got %d
What it means
GetBlob deliberately reads descriptor.Size+1 bytes so it can detect both truncation and inflation: if the number of bytes actually read differs from the descriptor's declared size, the registry served a body that does not match its own manifest, and the blob is rejected before any integrity hashing. This guards against rogue or buggy registries causing wrong-size data (and, with the +1 cap, unbounded allocation).
Source
Thrown at internal/oci/resolver.go:120
// blob content must be fetched directly from the blobs endpoint.
func GetBlob(ctx context.Context, resolver remotes.Resolver, ref reference.Named, descriptor spec.Descriptor) ([]byte, error) {
fetcher, err := resolver.Fetcher(ctx, ref.String())
if err != nil {
return nil, fmt.Errorf("creating fetcher for %s: %w", ref, err)
}
fetch, err := fetcher.Fetch(ctx, descriptor)
if err != nil {
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 {View on GitHub (pinned to ddc4b044b6)
Solutions
- Re-pull the artifact; if a proxy/CDN is in play, purge or bypass its cache.
- Re-push the artifact from the source so manifest sizes match actual blobs.
- Verify manually: compare Content-Length of GET /v2/<repo>/blobs/<digest> to descriptor.Size.
- Report persistent size skew to the registry operator — the manifest and blob store disagree.
Defensive patterns
Strategy: validation
Validate before calling
// trust, but verify: cross-check descriptor sizes against the registry
// HEAD /v2/<repo>/blobs/<digest> -> Content-Length must equal descriptor.Size
if contentLength != descriptor.Size {
return fmt.Errorf("registry size %d != manifest size %d; registry state inconsistent", contentLength, descriptor.Size)
} Try / catch
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))
// never use or persist mismatched bytes; re-pull or re-push
} Prevention
- Never bypass the size check when copying this pattern — it bounds allocation.
- Re-push artifacts after registry GC to keep manifests and blobs consistent.
- Purge or bypass caching proxies that serve partial bodies.
When it happens
Trigger: Registry returns fewer bytes than descriptor.Size (truncated blob, streaming bug) or more (pad/injection, wrong blob served, descriptor from a different manifest). Triggered by the comparison int64(len(content)) != descriptor.Size.
Common situations: Registry garbage collection raced with the pull; a caching proxy served a partial body; hand-crafted manifests with wrong layer sizes; registries with buggy range-request handling.
Related errors
- fetching blob %s: %w
- reading blob %s: %w
- invalid digest %s: %w
- blob digest mismatch: expected %s, got %s
- your Compose stack cannot be published as it only contains a
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/30e00d5b124603df.
Report an issue: GitHub.