docker/cli · error
could not add signer to repo
Error message
could not add signer to repo: %s: %w
What it means
Returned by addSignerToRepo() in `docker trust signer add` when addStagedSigner() fails to create the targets/<signerName> delegation role with the supplied public key(s) and the targets/releases delegation. %s is the signer name (role prefix stripped), %w is the notary staging error. Mirrors the bootstrap error in sign.go:214 but occurs in the explicit signer-add path on an already-initialized (or just-initialized) repo.
Solutions
- Check whether the signer already exists with `docker trust inspect <repo>` and remove it first if you intend to replace its keys.
- Ensure the --key file is a valid PEM public key generated for notary (see ingestPublicKeys parsing).
- Clear stale staged changes: notary stages changes; a previously failed add may have left pending changes — re-run or use a fresh client.
- Read the wrapped %w for the notary delegation error and address it (conflict, invalid key, etc.).
Example fix
// before $ docker trust signer add alice reg.io/app --key alice.pub Error: could not add signer to repo: alice: ... // after — remove existing signer then re-add with the new key $ docker trust signer remove alice reg.io/app -f $ docker trust signer add alice reg.io/app --key alice-new.pub
Defensive patterns
Strategy: validation
Validate before calling
// Before adding, check the signer role does not already exist (avoid conflict)
func signerExists(repo, name string) (bool, error) {
out, err := exec.Command("docker", "trust", "inspect", repo).CombinedOutput()
if err != nil { return false, err }
return strings.Contains(string(out), "\""+name+"\""), nil
} Try / catch
// If add fails on conflict, remove the existing signer then retry once
if out, err := exec.CommandContext(ctx, "docker", "trust", "signer", "add", name, repo, "--key", key).CombinedOutput(); err != nil {
if strings.Contains(string(out), "already") || strings.Contains(string(out), "conflict") {
_ = exec.CommandContext(ctx, "docker", "trust", "signer", "remove", name, repo, "-f").Run()
// retry add
}
} Prevention
- Inspect existing signers before adding to avoid delegation conflicts.
- Supply only valid PEM public keys (see ingestPublicKeys).
- When replacing a signer's key, remove then re-add rather than re-adding in place.
When it happens
Trigger: Running `docker trust signer add <name> <repo> --key <file>` where AddDelegationRoleAndKeys or AddDelegationPaths rejects the staging change — e.g. the delegation role already exists with a conflicting key set, or the staged metadata is malformed.
Common situations: Re-adding a signer whose role already exists; supplying a public key that does not match the expected PEM/type; partial trust state left over from a failed prior add.
Related errors
- failed to sign
- no valid signing keys for delegation roles
- could not remove signature for
- could not add signer to repo
- error removing signer from
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/14894d897b592aa7.
Report an issue: GitHub.
Appendix: source
Thrown at cmd/docker-trust/trust/signer_add.go:109
}
if _, err = notaryRepo.ListTargets(); err != nil {
switch err.(type) {
case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
_, _ = fmt.Fprintf(dockerCLI.Out(), "Initializing signed repository for %s...\n", repoName)
if err := getOrGenerateRootKeyAndInitRepo(notaryRepo); err != nil {
return trust.NotaryError(repoName, err)
}
_, _ = fmt.Fprintf(dockerCLI.Out(), "Successfully initialized %q\n", repoName)
default:
return trust.NotaryError(repoName, err)
}
}
newSignerRoleName := data.RoleName(path.Join(data.CanonicalTargetsRole.String(), signerName))
if err := addStagedSigner(notaryRepo, newSignerRoleName, signerPubKeys); err != nil {
return fmt.Errorf("could not add signer to repo: %s: %w", strings.TrimPrefix(newSignerRoleName.String(), "targets/"), err)
}
return notaryRepo.Publish()
}
func ingestPublicKeys(pubKeyPaths []string) ([]data.PublicKey, error) {
pubKeys := []data.PublicKey{}
for _, pubKeyPath := range pubKeyPaths {
// Read public key bytes from PEM file, limit to 1 KiB
pubKeyFile, err := os.OpenFile(pubKeyPath, os.O_RDONLY, 0o666)
if err != nil {
return nil, fmt.Errorf("unable to read public key from file: %w", err)
}
defer pubKeyFile.Close()
// limit to
l := io.LimitReader(pubKeyFile, 1<<20)
pubKeyBytes, err := io.ReadAll(l)
if err != nil {View on GitHub (pinned to 4f84911bfe)