docker/cli · warning

warning: potential malicious behavior - trust data has…

Error message

warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v

What it means

Returned by NotaryError for signed.ErrRoleThreshold (trust.go:251-252). TUF roles declare a threshold of signatures required to consider metadata valid; the downloaded role metadata did not meet its threshold count of valid signatures. Because insufficient signatures can indicate a compromised or tampered role, the client reports it as 'potential malicious behavior'.

Solutions

  1. Have the repository admin add the missing signature(s): each configured signer for the role must publish (AddTarget/Publish) so the role again carries threshold-valid signatures.
  2. If a signer key was legitimately removed, lower the role threshold to match the remaining keys via 'docker trust signer remove' followed by a publish, or add a replacement signer key.
  3. Confirm no unauthorized party modified the role metadata by reviewing the notary server's published targets.json and the list of valid signing key IDs.
  4. Re-pull after the admin re-publishes; if the warning persists, verify the local trust pinning config (trust-pinning.json) has not pinned an outdated root.
Defensive patterns

Strategy: validation

Validate before calling

// Verify the served role metadata meets its declared signature threshold before consuming.
func verifyRoleThreshold(repo client.Repository, roleName data.RoleName) error {
    roles, err := repo.ListRoles()
    if err != nil {
        return err
    }
    for _, r := range roles {
        if r.Name == roleName && len(r.Signatures) < r.Threshold {
            return fmt.Errorf("role %s below threshold (%d/%d)", roleName, len(r.Signatures), r.Threshold)
        }
    }
    return nil
}

Type guard

func isErrRoleThreshold(err error) bool {
    if err == nil {
        return false
    }
    return errors.Is(err, signed.ErrRoleThreshold)
}

Try / catch

if err := repo.Publish(); err != nil {
    if errors.Is(err, signed.ErrRoleThreshold) {
        return fmt.Errorf("insufficient signatures on published metadata: %w; have all required signers publish", err)
    }
    return trust.NotaryError(gun, err)
}

Prevention

When it happens

Trigger: Listing/pulling/verifying a repository whose targets/releases/delegation role requires N signatures but the served metadata only carries fewer valid ones - e.g. a delegation role had 2 keys with threshold 2 but one key was removed or one signature is invalid. Also triggered when a signer's key was rotated but the old signatures were not re-signed, dropping below threshold.

Common situations: Repository admin removed a signer key without lowering the role threshold, so remaining signatures are below the required count; a delegation role threshold was raised but not enough signers published; a compromised notary server stripped a signature; key rotation left metadata signed only by the new key while threshold still expects the old one.

Related errors


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

Appendix: source

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

	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.
func AddToAllSignableRoles(repo client.Repository, target *client.Target) error {
	signableRoles, err := GetSignableRoles(repo, target)
	if err != nil {
		return err

View on GitHub (pinned to 4f84911bfe)