{"record":{"id":"5619492f779ee8cb","repo":"docker/cli","slug":"unable-to-read-public-key-from-file-w","errorCode":null,"errorMessage":"unable to read public key from file: %w","messagePattern":"unable to read public key from file: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/docker-trust/trust/signer_add.go","lineNumber":121,"sourceCode":"\t\t}\n\t}\n\n\tnewSignerRoleName := data.RoleName(path.Join(data.CanonicalTargetsRole.String(), signerName))\n\n\tif err := addStagedSigner(notaryRepo, newSignerRoleName, signerPubKeys); err != nil {\n\t\treturn fmt.Errorf(\"could not add signer to repo: %s: %w\", strings.TrimPrefix(newSignerRoleName.String(), \"targets/\"), err)\n\t}\n\n\treturn notaryRepo.Publish()\n}\n\nfunc ingestPublicKeys(pubKeyPaths []string) ([]data.PublicKey, error) {\n\tpubKeys := []data.PublicKey{}\n\tfor _, pubKeyPath := range pubKeyPaths {\n\t\t// Read public key bytes from PEM file, limit to 1 KiB\n\t\tpubKeyFile, err := os.OpenFile(pubKeyPath, os.O_RDONLY, 0o666)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to read public key from file: %w\", err)\n\t\t}\n\t\tdefer pubKeyFile.Close()\n\t\t// limit to\n\t\tl := io.LimitReader(pubKeyFile, 1<<20)\n\t\tpubKeyBytes, err := io.ReadAll(l)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to read public key from file: %w\", err)\n\t\t}\n\n\t\t// Parse PEM bytes into type PublicKey\n\t\tpubKey, err := tufutils.ParsePEMPublicKey(pubKeyBytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not parse public key from file: %s: %w\", pubKeyPath, err)\n\t\t}\n\t\tpubKeys = append(pubKeys, pubKey)\n\t}\n\treturn pubKeys, nil\n}","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cmd/docker-trust/trust/signer_add.go#L103-L139","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n$ docker trust signer add alice reg.io/app --key alice.pub\nError: unable to read public key from file: open alice.pub: no such file or directory\n\n// after\n$ docker trust signer add alice reg.io/app --key /home/alice/.docker/trust/keys/alice.pub","handlingStrategy":"validation","validationCode":"// Verify the key file is openable before invoking signer add\nfunc checkKeyFile(path string) error {\n    f, err := os.Open(path)\n    if err != nil { return fmt.Errorf(\"key file %s: %w\", path, err) }\n    f.Close()\n    return nil\n}","typeGuard":"func keyFileReadable(path string) bool {\n    info, err := os.Stat(path)\n    if err != nil || info.IsDir() { return false }\n    return true\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["docker","trust","filesystem","key-management","validation"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}