{"record":{"id":"14f539a9311504a8","repo":"docker/cli","slug":"cannot-push-a-digest-reference","errorCode":null,"errorMessage":"cannot push a digest reference","messagePattern":"cannot push a digest reference","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/docker-trust/internal/trust/trust_push.go","lineNumber":75,"sourceCode":"\t\terr := json.Unmarshal(*msg.Aux, &pushResult)\n\t\tif err == nil && pushResult.Tag != \"\" {\n\t\t\tif dgst, err := digest.Parse(pushResult.Digest); err == nil {\n\t\t\t\th, err := hex.DecodeString(dgst.Hex())\n\t\t\t\tif err != nil {\n\t\t\t\t\tnotaryTarget = nil\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tnotaryTarget.Name = pushResult.Tag\n\t\t\t\tnotaryTarget.Hashes = data.Hashes{string(dgst.Algorithm()): h}\n\t\t\t\tnotaryTarget.Length = int64(pushResult.Size)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar tag string\n\tswitch x := ref.(type) {\n\tcase reference.Digested:\n\t\treturn errors.New(\"cannot push a digest reference\")\n\tcase reference.Tagged:\n\t\ttag = x.Tag()\n\tdefault:\n\t\t// We want trust signatures to always take an explicit tag,\n\t\t// otherwise it will act as an untrusted push.\n\t\tif err := jsonstream.Display(ctx, in, ioStreams.Out()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, _ = fmt.Fprintln(ioStreams.Err(), \"No tag specified, skipping trust metadata push\")\n\t\treturn nil\n\t}\n\n\tif err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil {\n\t\treturn err\n\t}\n\n\tif cnt > 1 {\n\t\treturn errors.New(\"internal error: only one call to handleTarget expected\")","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cmd/docker-trust/internal/trust/trust_push.go#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Push by tag instead of digest: 'docker push repo/image:tag'.","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.","Disable DCT for the specific operation only if signing is not required: DOCKER_CONTENT_TRUST=0."],"exampleFix":"# before: docker push myrepo/img@sha256:abcd...\n# after:  docker tag myrepo/img@sha256:abcd... myrepo/img:v1 && docker push myrepo/img:v1","handlingStrategy":"validation","validationCode":"// Reject digest references before attempting a trusted push\nfunc isDigestRef(ref reference.Named) bool {\n\t_, ok := ref.(reference.Digested)\n\treturn ok\n}\n// if isDigestRef(ref) { return errors.New(\"use a tag, not a digest, for trusted push\") }","typeGuard":"// Type guard narrowing to a taggable reference\nfunc isTagged(ref reference.Named) bool {\n\t_, ok := ref.(reference.Tagged)\n\treturn ok\n}","tryCatchPattern":null,"preventionTips":["Always push by tag when DOCKER_CONTENT_TRUST is enabled.","Resolve references to tags, not digests, before trusted push.","In code, type-switch on the reference and reject reference.Digested in the push path."],"tags":["trust","notary","push","digest","reference"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}