Skip to main content
DevOps Insightsguides & answers

Sigstore / cosign · troubleshooting

Error: no matching signatures

cosign found no signature on the image that satisfies the identity you asked it to accept. Either nothing signed it, or something did and you are checking for a different signer — and the full message tells you which: no signatures found means nothing is attached, while no matching signatures: none of the expected identities matched is followed by the subject and issuer that actually signed.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 10 sections

Before you start

  • cosign 2.0 or later (cosign version). From 2.0 a keyless verify needs an identity flag and an issuer flag, and cosign v3 stores container signatures as OCI referrers by default.
  • Read access to the registry, for the image and for the signatures stored beside it.
  • The identity and issuer your signing pipeline uses. For a GitHub Actions workflow the identity looks like https://github.com/<org>/<repo>/.github/workflows/<file>@refs/heads/<branch> and the issuer is https://token.actions.githubusercontent.com.
  • The image digest the signing job reported, so you verify the exact image that was signed rather than whatever a tag points to now.
  • Network access from the verifying host to Sigstore's trust root, or a trusted root file passed with --trusted-root when it is offline.

Run this first

step 1 of 3
cosign verify <image> --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'

Look for Success, and the certificate subject and issuer it prints

Separates the two situations the error conflates. Succeeding means a signature exists and your identity match was wrong; failing means there is nothing there to match.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

Does cosign verify succeed when you match the identity and issuer with '.*' regexps?

This asks only 'is there any signature at all', separating a missing signature from a mismatched identity.

Cause space

5 of 5 still possible

  • The identity or issuer you are matching is not the one that signedCommon
  • Nothing signed this imageCommon
  • The command omits the identity flags entirelyOccasional
  • You are verifying a different image than you thinkOccasional
  • The transparency log cannot be reached or consultedRare

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Check it with a tool

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    cosign verify <image> --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'

    Separates the two situations the error conflates. Succeeding means a signature exists and your identity match was wrong; failing means there is nothing there to match.

    Look for Success, and the certificate subject and issuer it prints

  2. Step 2

    cosign tree <image>

    Lists the signatures, attestations and SBOMs the registry holds for the image, or says No Supply Chain Security Related Artifacts found. Run it again with --registry-referrers-mode oci-1-1 to include signatures stored as OCI referrers.

    Look for A reference the registry actually holds

  3. Step 3

    crane digest <image>:<tag>

    Confirms the tag still resolves to the digest that was signed. A rebuilt tag is the quiet version of this failure.

    Look for A digest matching the one the signing job reported

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

The identity or issuer you are matching is not the one that signed

Common

The signature exists and is valid; it simply was not made by the identity in your flags. A workflow moved to a different repository or ref, the signing job was renamed, or the issuer is written as one of the several forms a provider can present. cosign compares these as strings, so near enough is not enough.

Confirm

cosign verify <image> --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*'

Whether verification now succeeds. If it does, a signature exists and the identity you were matching is the thing that was wrong

Fix

  • Read the identity out of the error rather than constructing it: for an identity mismatch cosign's message ends got subjects [...] with issuer ..., built from the certificate that actually signed.
  • Match that exact value, or a regexp deliberately anchored to it — not a wildcard left in place after debugging.
  • If signing moved between repositories or refs, the identity changed with it and the verification side has to change too.
cosign verify <image> --certificate-identity '<exact subject from above>' --certificate-oidc-issuer '<exact issuer>'

Nothing signed this image

Common

The signing step never ran, ran on a different tag, or ran on a digest that a later rebuild replaced. Because signatures attach to a digest, re-pushing the same tag from a new build leaves the old signature pointing at an image nobody is pulling any more — so the tag looks signed in the registry's history and verifies as unsigned today.

Confirm

cosign tree <image>

A Signatures for an image tag line, or, when run with --registry-referrers-mode oci-1-1, an artifacts via OCI referrer line. If neither run lists a signature, the image carries none

Fix

  • Confirm the signing step ran in the build that produced the digest you are verifying, not merely in some earlier build of the same tag.
  • Sign by digest and verify by digest wherever the pipeline allows it; a mutable tag makes this failure recurrent and confusing.
  • Check the signing step's own exit status — a signature step that failed quietly leaves exactly this state.

The command omits the identity flags entirely

Occasional

Keyless verification needs an identity flag and an issuer flag, each in its exact or -regexp form. cosign 2.0 and later refuse a keyless verify that carries neither, with --certificate-identity or --certificate-identity-regexp is required for verification in keyless mode, so this failure reads differently from no matching signatures. This is most often inherited: a snippet written for cosign 1.x, or copied from a key-based example where the flags do not apply.

Confirm

history | grep 'cosign verify' | tail -5

A verify command with neither --certificate-identity nor --key — which cannot express what it is meant to accept

Fix

  • Add both flags. Sigstore documents them as the shape of a keyless verification.
  • If the image is signed with a key rather than keylessly, use --key instead; the two modes are not interchangeable.

You are verifying a different image than you think

Occasional

A tag resolved at verification time can differ from the one signed minutes earlier — a parallel build, a mirror that lags, or a multi-arch index where the signature sits on one manifest and the pull resolves to another. The verification is honest; the reference is ambiguous.

Confirm

crane digest <image>:<tag>

Whether this digest matches the one the signing job recorded in its output

Fix

  • Pin to a digest end to end: sign the digest, promote the digest, verify the digest.
  • For multi-arch images, confirm whether your policy expects the index or the per-platform manifest to carry the signature.

The transparency log cannot be reached or consulted

Rare

Keyless verification checks that the signature was recorded in a transparency log, normally using the inclusion proof carried with the signature rather than a live lookup. In an air-gapped or egress-restricted environment it can still fail if the signature lacks that proof or the trust root cannot be fetched; the text after no matching signatures: gives the reason. Distinguished by affecting every image at once, including ones known to be signed.

Confirm

cosign verify <image> --certificate-identity '<id>' --certificate-oidc-issuer '<issuer>' --verbose

Mentions of Rekor, the transparency log, or a timeout reaching it, rather than an identity mismatch

Fix

  • Give the verifying host egress to the transparency log, or run and point at your own instance.
  • Treat any flag that skips the log as a change in what you are asserting, not as a workaround — record why it was needed.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

cosign checks that an image carries a signature made by a signer you named. This error means it found no signature from that signer. Either nothing signed the image, or something did and you described the signer differently, and the rest of the error message tells you which.

Words used on this page

Keyless signing
Signing with a short-lived certificate tied to an OIDC identity, such as a CI workflow, instead of a long-lived key. Sigstore's Fulcio issues the certificate, valid for 10 minutes.
Certificate identity
The signer named in that certificate, such as an email address or a workflow URL. --certificate-identity, or --certificate-identity-regexp, says which one you accept.
OIDC issuer
Who vouched for the identity, for example https://token.actions.githubusercontent.com for GitHub Actions. --certificate-oidc-issuer, or its -regexp form, says which one you accept.
Digest
The sha256: hash that names one exact image. Signatures attach to a digest, not to a tag, so a re-pushed tag can point at an unsigned image.
Transparency log (Rekor)
A public, append-only record of signing events. Verification checks that the signature was logged; --insecure-ignore-tlog turns that check off.
OCI referrer
A registry feature that links an artifact, such as a signature, to the image it describes. cosign v3 stores container signatures this way instead of under a sha256-<digest>.sig tag.
What is actually happening

Verification is not a yes/no question about whether an image is signed. It asks a narrower one: is there a signature on this image, made by this identity, issued by this OIDC provider. "No matching signatures" is followed by the reason each signature was rejected. For an identity mismatch it prints the subject and issuer that actually signed, so read the whole line.

That ambiguity is the reason this page exists. Two very different situations both fail verification: an image nobody signed (no signatures found) and an image signed correctly by a pipeline whose identity you spelled differently (no matching signatures: none of the expected identities matched). The wording differs, so read past the first few words. The first is a supply-chain finding. The second is a typo. Treating them the same way is how verification ends up switched off.

cosign's reference is explicit that keyless verification needs an identity (--certificate-identity or --certificate-identity-regexp) and an issuer (--certificate-oidc-issuer or --certificate-oidc-issuer-regexp). They are not optional hardening — since cosign 2.0 a keyless verify without them is refused outright, with --certificate-identity or --certificate-identity-regexp is required for verification in keyless mode.

Cosign 2.0 tightened this deliberately. If a command that worked on an older cosign now fails, the binary changed under you rather than the image.

How to tell this is your problem
WhereWhat you see
cosign verify"Error: no matching signatures", usually with a non-zero exit
A CI gateThe verify step failing while the build and push steps succeeded
Kubernetes admissionA policy controller rejecting the pod, with the image named but no signer detail

How to know it is actually fixed

  • cosign verify succeeds with the real identity and issuer in place — not with a regexp wildcard left over from debugging.
  • The same command run against a deliberately unsigned image still fails. A check that passes everything is not a check.
  • If a policy controller enforces this, deploy an unsigned image to a scratch namespace and confirm it is refused.

Stopping it happening again

  • Sign and verify by digest. Tags are mutable and this is the failure mode that mutability produces.
  • Keep the verifying identity next to the signing identity in the same repository, so a change to one is visible in the diff of the other.
  • Fail the pipeline when signing fails. An unsigned image that ships is worse than a build that stops.
  • Never resolve a verification failure by removing the verification. Record what was wrong, then restore the check.

Security considerations

  • A run with --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*' accepts a signature from anyone who can get a Sigstore certificate. If it passes, compare the subject named in the original error with your pipeline before calling it a typo; an identity you do not recognise is a supply-chain finding.
  • Anchor every regexp with ^ and $. cosign matches identities with Go's MatchString, which accepts a match anywhere in the string, so an unanchored pattern such as my-org also accepts https://github.com/my-org-fork/....
  • Pin the issuer exactly, and match the ref you release from. A GitHub Actions identity ends in the workflow's ref, such as @refs/heads/main, so a pattern that accepts any ref accepts signatures from any branch someone can push and run the workflow on.
  • Treat --insecure-ignore-tlog and --insecure-ignore-sct as a change to what you are proving, not as a fix. cosign's own help says artifacts cannot be publicly verified when they are not in a log.
  • Sign and verify by digest. cosign's signing reference says to sign @sha256:... rather than a tag so that you sign what you think you are signing, and a tag verified later may point somewhere else.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it