docker/compose · error
invalid digest %s: %w
Error message
invalid digest %s: %w
What it means
Before hashing the downloaded bytes, GetBlob calls descriptor.Digest.Validate() to check that the digest string itself is well-formed (algorithm:hex with correct length/charset). A malformed digest — missing algorithm, wrong hex length for the algorithm, invalid characters, empty string — fails here. This is a descriptor-quality check distinct from content verification (error 97).
Source
Thrown at internal/oci/resolver.go:125
}
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 {
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)View on GitHub (pinned to ddc4b044b6)
Solutions
- Use full, canonical digests: sha256:<64 lowercase hex chars>.
- Generate descriptors from real manifests (resolver.Resolve) rather than constructing them manually.
- Validate digests at ingestion time when reading them from user-supplied files.
- Check for truncation (e.g. 12-char short IDs are not valid full digests).
Example fix
// before
desc := spec.Descriptor{Digest: "sha256:deadbeef", Size: 123} // invalid hex length
// after
dg, _ := digest.Parse("sha256:<full-64-hex>")
desc := spec.Descriptor{Digest: dg, Size: 123} Defensive patterns
Strategy: validation
Validate before calling
if err := descriptor.Digest.Validate(); err != nil {
return fmt.Errorf("descriptor carries malformed digest %q: %w", descriptor.Digest, err)
} Type guard
func isValidDigest(s string) bool {
d, err := digest.Parse(s)
return err == nil && d.Validate() == nil
} Try / catch
if err := descriptor.Digest.Validate(); err != nil {
return nil, fmt.Errorf("invalid digest %s: %w", descriptor.Digest, err)
// reject the descriptor; do not attempt a fetch with it
} Prevention
- Always digest.Parse/Validate strings coming from config or user input.
- Never hand-assemble digests; derive them from real content or manifests.
- Reject short IDs (12-hex) where full digests are required.
When it happens
Trigger: Passing a spec.Descriptor whose Digest string is like "sha256:xyz", "abc123" (no algorithm), "sha512:" (empty hex), or hex of the wrong length for the declared algorithm.
Common situations: Descriptors assembled by hand or parsed from custom YAML/JSON instead of from a registry manifest; truncated digest strings from config files; copy/paste typos in annotations or lockfiles.
Related errors
- blob %s size mismatch: expected %d bytes, got %d
- blob digest mismatch: expected %s, got %s
- OCI index %s doesn't refer to compose artifacts
- %s is not a compose project OCI artifact, but %s
- missing annotation com.docker.compose.envfile in layer %q
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/2954c624f5b737c7.
Report an issue: GitHub.