docker/cli · error

error: could not find signing keys for remote repository

Error message

error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v

What it means

Returned by NotaryError when the notary client raises signed.ErrNoKeys: the local trust store contains no signing key for the repository role, or the key that is present could not be decrypted with the supplied passphrase. This maps to the ErrNoKeys case in the type switch at trust.go:247-248, so it surfaces during any operation that needs to sign or rotate a role (push, init, signer add). The error wraps the underlying notary error so the exact role/key identifier is visible in %v.

Solutions

  1. Check that the key exists locally: ls ~/.docker/trust/private and confirm a key file for the GUN/role is present; if missing, import it with 'docker trust key load <key.file>' or recover the root key from backup.
  2. Set the correct passphrase env var: export DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=<repo-passphrase> (and DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE for root-role operations) before retrying.
  3. If the key is on a Yubikey, ensure it is plugged in and the yubikey-agent/pcscd service is running, then retry.
  4. If the repository was never initialized from this machine and you do not have the key, re-initialize with a new root key only if you accept losing prior signatures: back up then remove ~/.docker/trust/private for that GUN and run a trusted push to bootstrap new metadata.
  5. Verify the key is not corrupt by loading it explicitly with 'notary key list' or 'docker trust key load' and confirming the role/key ID matches the notary repo.

Example fix

# before: passphrase missing, push fails with ErrNoKeys
DOCKER_CONTENT_TRUST=1 docker push myrepo/img:tag
# after: supply passphrase env vars
export DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE=$ROOT_PASS
export DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$REPO_PASS
DOCKER_CONTENT_TRUST=1 docker push myrepo/img:tag
Defensive patterns

Strategy: validation

Validate before calling

// Before push, ensure a signing key exists for the GUN and the passphrase resolves.
func ensureSigningKeyAvailable(repo client.Repository, passphrase string) error {
    keys := repo.GetCryptoService().ListKeys(data.CanonicalTargetsRole)
    if len(keys) == 0 {
        return fmt.Errorf("no targets signing key in local store; import with 'docker trust key load'")
    }
    // Probe decrypt by attempting to list all keys (forces decryption lazily);
    // for a stricter check, exercise GetKey on each ID.
    for _, id := range keys {
        if _, _, err := repo.GetCryptoService().GetSignerKey(data.CanonicalTargetsRole); err != nil {
            return fmt.Errorf("cannot decrypt key %s: %w; verify DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE", id, err)
        }
    }
    return nil
}

Type guard

// Narrow a notary error to the ErrNoKeys case so callers can react specifically.
func isErrNoKeys(err error) bool {
    if err == nil {
        return false
    }
    return errors.Is(err, signed.ErrNoKeys)
}

Try / catch

if err := repo.Publish(); err != nil {
    if errors.Is(err, signed.ErrNoKeys) {
        // surface a guided message: import key / set passphrase
        return fmt.Errorf("signing key missing or undecryptable: %w; set DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE", err)
    }
    return trust.NotaryError(gun, err)
}

Prevention

When it happens

Trigger: Calling repo.Publish(), repo.AddTarget(), repo.RotateKey(), or repo.Initialize() when (a) the local key store (~/.docker/trust/private) has no key for the required role, or (b) the DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE / ROOT_PASSPHRASE env var is wrong/missing so decryption of the on-disk encrypted key fails. Also triggered on a fresh machine that has never imported the repository's signing key but tries to publish to an already-initialized notary repo.

Common situations: Developer switches machines without copying ~/.docker/trust/private; CI runner has a clean home dir and only the image pushed before, so it lacks the targets/snapshot key; passphrase env vars were rotated or mistyped in the pipeline; key was generated on a Yubikey that is now unplugged; Docker Content Trust repository passphrase was forgotten so decryption silently fails.

Related errors


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

Appendix: source

Thrown at cmd/docker-trust/internal/trust/trust.go:248

// NotaryError formats an error message received from the notary service
func NotaryError(repoName string, err error) error {
	switch err.(type) {
	case *json.SyntaxError:
		logrus.Debugf("Notary syntax error: %s", err)
		return fmt.Errorf("error: no trust data available for remote repository %s. Try running notary server and setting DOCKER_CONTENT_TRUST_SERVER to its HTTPS address", repoName)
	case signed.ErrExpired:
		return fmt.Errorf("error: remote repository %s out-of-date: %v", repoName, err)
	case trustmanager.ErrKeyNotFound:
		return fmt.Errorf("error: signing keys for remote repository %s not found: %v", repoName, err)
	case storage.NetworkError:
		return fmt.Errorf("error: error contacting notary server: %v", err)
	case storage.ErrMetaNotFound:
		return fmt.Errorf("error: trust data missing for remote repository %s or remote repository not found: %v", repoName, err)
	case trustpinning.ErrRootRotationFail, trustpinning.ErrValidationFail, signed.ErrInvalidKeyType:
		return fmt.Errorf("warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
	case signed.ErrNoKeys:
		return fmt.Errorf("error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
	case signed.ErrLowVersion:
		return fmt.Errorf("warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
	case signed.ErrRoleThreshold:
		return fmt.Errorf("warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
	case client.ErrRepositoryNotExist:
		return fmt.Errorf("error: remote trust data does not exist for %s: %v", repoName, err)
	case signed.ErrInsufficientSignatures:
		return fmt.Errorf("error: could not produce valid signature for %s.  If Yubikey was used, was touch input provided?: %v", repoName, err)
	default:
		return err
	}
}

// AddToAllSignableRoles attempts to add the image target to all the top level
// delegation roles we can (based on whether we have the signing key and whether
// the role's path allows us to).
//
// If there are no delegation roles, we add to the targets role.

View on GitHub (pinned to 4f84911bfe)