containerd/containerd · error · errdefs.ErrInvalidArgument
unable to fetch descriptor (%s) which reports content size o
Error message
unable to fetch descriptor (%s) which reports content size of zero: %w
What it means
Fetch rejects a descriptor that reports Size 0, because a zero-length blob cannot be meaningfully committed to the content store and almost always indicates the registry responded without a Content-Length header (poorly configured front end or proxy). Throwing early here avoids a more confusing downstream error.
Source
Thrown at core/remotes/handlers.go:156
func Fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc ocispec.Descriptor) error {
log.G(ctx).Debug("fetch")
cw, err := content.OpenWriter(ctx, ingester, content.WithRef(MakeRefKey(ctx, desc)), content.WithDescriptor(desc))
if err != nil {
return err
}
defer cw.Close()
ws, err := cw.Status()
if err != nil {
return err
}
if desc.Size == 0 {
// most likely a poorly configured registry/web front end which responded with no
// Content-Length header; unable (not to mention useless) to commit a 0-length entry
// into the content store. Error out here otherwise the error sent back is confusing
return fmt.Errorf("unable to fetch descriptor (%s) which reports content size of zero: %w", desc.Digest, errdefs.ErrInvalidArgument)
}
if ws.Offset == desc.Size {
// If writer is already complete, commit and return
err := cw.Commit(ctx, desc.Size, desc.Digest)
if err != nil && !errdefs.IsAlreadyExists(err) {
return fmt.Errorf("failed commit on ref %q: %w", ws.Ref, err)
}
return err
}
if desc.Size == int64(len(desc.Data)) {
return content.Copy(ctx, cw, bytes.NewReader(desc.Data), desc.Size, desc.Digest)
}
rc, err := fetcher.Fetch(ctx, desc)
if err != nil {
return err
}View on GitHub (pinned to 4246446a2b)
Solutions
- Fix the registry/proxy to return Content-Length headers for GETs (disable chunked re-encoding)
- Check for proxies like nginx stripping headers; ensure Content-Length is passed through
- Verify the image descriptor is valid in the source registry (re-pull manifest)
- Use a registry that sets Content-Length correctly (Docker Hub, Harbor, recent distribution releases)
Example fix
// before: nginx proxy strips headers
// proxy_pass http://registry;
// after: pass Content-Length through
location /v2/ {
proxy_pass http://registry;
proxy_set_header Content-Length $content_length;
} Defensive patterns
Strategy: validation
Validate before calling
if desc.Size == 0 {
// fix registry/proxy config before fetching
return errors.New("descriptor reports zero size: registry not returning Content-Length")
} Type guard
func hasValidSize(desc ocispec.Descriptor) bool {
return desc.Size > 0
} Try / catch
err := handlers.Fetch(ctx, ingester, fetcher, desc)
if err != nil && strings.Contains(err.Error(), "content size of zero") {
// fix registry/proxy; try direct registry access bypassing front end
return fetchDirect(ctx, desc)
} Prevention
- Verify registry front end passes Content-Length headers through
- Test manifest/blob GETs with curl -I to confirm Content-Length present
- Avoid proxies/CDN layers that chunk responses without length
- Pin to recent registry distribution versions that set Content-Length
When it happens
Trigger: Calling Fetch (via HandlerFunc/handlers) with a workspace descriptor whose desc.Size == 0 — typically when the registry or a web front end omitted Content-Length for the manifest/blob request.
Common situations: Registries behind proxies/CDNs that strip Content-Length; misconfigured reverse proxies; chunked responses to manifest GETs; self-hosted registries misconfigured with wrong headers.
Related errors
- no realm specified for token auth challenge
- unsupported Content-Encoding algorithm:
- failed to discard to offset: %w
- writer has been reset
- unable to discard to offset
AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02).
Data as JSON: /api/errors/b38bbd86d06b213e.
Report an issue: GitHub.