containerd/containerd · error · errdefs.ErrNotFound
no push hosts: %w
Error message
no push hosts: %w
What it means
containerd's docker pusher filters its configured registry hosts down to those with the push capability. If no host declares push capability, push() aborts immediately with errdefs.ErrNotFound wrapped in this message before any network request is made. It means the remotes configuration simply has no usable push endpoint, not a network or auth problem.
Source
Thrown at core/remotes/docker/pusher.go:109
status, err := p.tracker.GetStatus(ref)
if err == nil {
if status.Committed && status.Offset == status.Total {
return nil, fmt.Errorf("ref %v: %w", ref, errdefs.ErrAlreadyExists)
}
if unavailableOnFail && status.ErrClosed == nil {
// Another push of this ref is happening elsewhere. The rest of function
// will continue only when `errdefs.IsNotFound(err) == true` (i.e. there
// is no actively-tracked ref already).
return nil, fmt.Errorf("push is on-going: %w", errdefs.ErrUnavailable)
}
// TODO: Handle incomplete status
} else if !errdefs.IsNotFound(err) {
return nil, fmt.Errorf("failed to get status: %w", err)
}
hosts := p.filterHosts(HostCapabilityPush)
if len(hosts) == 0 {
return nil, fmt.Errorf("no push hosts: %w", errdefs.ErrNotFound)
}
var (
isManifest bool
existCheck []string
host = hosts[0]
)
if images.IsManifestType(desc.MediaType) || images.IsIndexType(desc.MediaType) {
isManifest = true
existCheck = getManifestPath(p.object, desc.Digest)
} else {
existCheck = []string{"blobs", desc.Digest.String()}
}
req := p.request(host, http.MethodHead, existCheck...)
if err := req.addNamespace(p.refspec.Hostname()); err != nil {
return nil, errView on GitHub (pinned to 4246446a2b)
Solutions
- Add or fix the host configuration so at least one host includes docker.HostCapabilityPush in its Capabilities.
- Verify the registry host is actually configured for the reference's hostname (check containerd config.toml [plugins."io.containerd.grpc.v1.cri".registry] or client.WithRegistryHost).
- If you only intend to pull, switch the operation to a pull path; push cannot proceed without a push-capable host.
- Confirm you are not overriding hosts with client.WithScheme/WithHosts incorrectly (scheme/host typo can yield an empty match).
Example fix
// before
hosts := []docker.Host{{Host: "mirror.example.com", Capabilities: docker.HostCapabilityPull}}
// after
hosts := []docker.Host{{Host: "registry.example.com", Capabilities: docker.HostCapabilityPull | docker.HostCapabilityPush}} Defensive patterns
Strategy: validation
Validate before calling
// before pushing, verify a push-capable host exists for the ref
hosts := pusherConfig.Hosts(refspec.Hostname())
if len(hosts) == 0 {
return fmt.Errorf("no host configured for %s", refspec.Hostname())
}
for _, h := range hosts {
if h.Capabilities&docker.HostCapabilityPush != 0 {
return nil // ok
}
}
return fmt.Errorf("no push-capable host for %s", refspec.Hostname()) Try / catch
if err := pusher.Push(ctx, desc); err != nil {
if strings.Contains(err.Error(), "no push hosts") {
// fix host config, do not retry
}
return err
} Prevention
- Always include HostCapabilityPush in host capabilities for registries you intend to push to
- Mirror-only configs are pull-only: use a separate push-capable host entry
- Unit-test host resolution for each registry hostname you push to
When it happens
Trigger: Calling Writer (content store writer) or Push with a resolver/host config where every configured host lacks HostCapabilityPush — e.g. hosts built as mirror/pull-only, or an empty hosts list for the registry.
Common situations: Configuring containerd with registry.mirrors (pull-only) and then trying to push; using a client.Config host entry without docker.HostCapabilityPush; pushing through a proxy endpoint that was set up solely for pulling; mis-resolved default host (e.g. docker.io not configured).
Related errors
- cannot truncate remote upload
- content %v on remote: %w
- pushing with mount from %s: %w
- push access denied, repository does not exist or may require
- unable to parse location %v: %w
AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02).
Data as JSON: /api/errors/d9618731691d8377.
Report an issue: GitHub.