docker/cli · error

cannot push a digest reference

Error message

cannot push a digest reference

What it means

Returned by PushTrustedReference when the image reference is a digest reference (reference.Digested, e.g. image@sha256:abc...). Docker Content Trust signs tags, not content-addressable digests, so a digest reference has no tag to attach a signature to and is rejected before any push.

Solutions

  1. Push by tag instead of digest: 'docker push repo/image:tag'.
  2. If you must move a digest, push the manifest by tag first so trust can attach to the tag, then reference the digest for pulls.
  3. Disable DCT for the specific operation only if signing is not required: DOCKER_CONTENT_TRUST=0.

Example fix

# before: docker push myrepo/img@sha256:abcd...
# after:  docker tag myrepo/img@sha256:abcd... myrepo/img:v1 && docker push myrepo/img:v1
Defensive patterns

Strategy: validation

Validate before calling

// Reject digest references before attempting a trusted push
func isDigestRef(ref reference.Named) bool {
	_, ok := ref.(reference.Digested)
	return ok
}
// if isDigestRef(ref) { return errors.New("use a tag, not a digest, for trusted push") }

Type guard

// Type guard narrowing to a taggable reference
func isTagged(ref reference.Named) bool {
	_, ok := ref.(reference.Tagged)
	return ok
}

Prevention

When it happens

Trigger: Performing a trusted push (DOCKER_CONTENT_TRUST=1) of an image specified by digest, e.g. 'docker push repo/image@sha256:...'. The type switch at trust_push.go:73 hits the Digested case and returns immediately.

Common situations: Pin-to-digest CI pipelines that also enable content trust; scripting that resolves an image to a digest and then pushes under DCT; copy-pasting a digest reference where a tag is expected.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/14f539a9311504a8. Report an issue: GitHub.

Appendix: source

Thrown at cmd/docker-trust/internal/trust/trust_push.go:75

		err := json.Unmarshal(*msg.Aux, &pushResult)
		if err == nil && pushResult.Tag != "" {
			if dgst, err := digest.Parse(pushResult.Digest); err == nil {
				h, err := hex.DecodeString(dgst.Hex())
				if err != nil {
					notaryTarget = nil
					return
				}
				notaryTarget.Name = pushResult.Tag
				notaryTarget.Hashes = data.Hashes{string(dgst.Algorithm()): h}
				notaryTarget.Length = int64(pushResult.Size)
			}
		}
	}

	var tag string
	switch x := ref.(type) {
	case reference.Digested:
		return errors.New("cannot push a digest reference")
	case reference.Tagged:
		tag = x.Tag()
	default:
		// We want trust signatures to always take an explicit tag,
		// otherwise it will act as an untrusted push.
		if err := jsonstream.Display(ctx, in, ioStreams.Out()); err != nil {
			return err
		}
		_, _ = fmt.Fprintln(ioStreams.Err(), "No tag specified, skipping trust metadata push")
		return nil
	}

	if err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil {
		return err
	}

	if cnt > 1 {
		return errors.New("internal error: only one call to handleTarget expected")

View on GitHub (pinned to 4f84911bfe)