docker/cli · error
unable to read public key from file
Error message
unable to read public key from file: %w
What it means
Returned by ingestPublicKeys() in `docker trust signer add` when os.OpenFile() fails to open a --key path read-only. %w wraps the OS error. This fires before any byte is read (the file could not be opened at all) — distinct from signer_add.go:128 which fires when OpenFile succeeds but ReadAll fails.
Solutions
- Verify the path exists and is a file: `ls -l <path>`.
- Use an absolute path to avoid working-directory ambiguity.
- Fix permissions/ownership so the current user can read it (e.g. `chmod u+r <path>`).
- Quote paths containing spaces.
Example fix
// before $ docker trust signer add alice reg.io/app --key alice.pub Error: unable to read public key from file: open alice.pub: no such file or directory // after $ docker trust signer add alice reg.io/app --key /home/alice/.docker/trust/keys/alice.pub
Defensive patterns
Strategy: validation
Validate before calling
// Verify the key file is openable before invoking signer add
func checkKeyFile(path string) error {
f, err := os.Open(path)
if err != nil { return fmt.Errorf("key file %s: %w", path, err) }
f.Close()
return nil
} Type guard
func keyFileReadable(path string) bool {
info, err := os.Stat(path)
if err != nil || info.IsDir() { return false }
return true
} Prevention
- Use absolute paths for --key.
- Store keys under a dedicated readable directory like ~/.docker/trust.
- Validate the path exists and is a regular file in CI before the sign step.
When it happens
Trigger: Passing `--key <path>` where the path does not exist, is a directory, or the process lacks read permission. Also when the path has a typo or is relative to an unexpected working directory.
Common situations: Typo in the key path; key file on a different host/container not mounted; permissions too restrictive (0600 owned by another user); path with spaces unquoted.
Related errors
- public key file already exists
- got a device
- public key path does not exist
- refusing to load key from
- no tag specified for
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/5619492f779ee8cb.
Report an issue: GitHub.
Appendix: source
Thrown at cmd/docker-trust/trust/signer_add.go:121
}
}
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 {
return nil, fmt.Errorf("unable to read public key from file: %w", err)
}
// Parse PEM bytes into type PublicKey
pubKey, err := tufutils.ParsePEMPublicKey(pubKeyBytes)
if err != nil {
return nil, fmt.Errorf("could not parse public key from file: %s: %w", pubKeyPath, err)
}
pubKeys = append(pubKeys, pubKey)
}
return pubKeys, nil
}View on GitHub (pinned to 4f84911bfe)