{"record":{"id":"2790c973e45b6e73","repo":"getsops/sops","slug":"no-key-to-unlock","errorCode":null,"errorMessage":"no key to unlock","messagePattern":"no key to unlock","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pgp/keysource.go","lineNumber":589,"sourceCode":"\t\t\t\tlog.Errorf(\"failed to close connection with gpg-agent: %s\", err)\n\t\t\t}\n\t\t}(conn)\n\n\t\tfor _, k := range keys {\n\t\t\treq := gpgagent.PassphraseRequest{\n\t\t\t\tCacheKey: k.PublicKey.KeyIdShortString(),\n\t\t\t\tPrompt:   \"Passphrase\",\n\t\t\t\tDesc:     fmt.Sprintf(\"Unlock key %s to decrypt sops's key\", k.PublicKey.KeyIdShortString()),\n\t\t\t}\n\t\t\tpass, err := conn.GetPassphrase(&req)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"gpg-agent passphrase request errored: %s\", err)\n\t\t\t}\n\t\t\tk.PrivateKey.Decrypt([]byte(pass))\n\t\t\treturn []byte(pass), nil\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"no key to unlock\")\n\t}\n}\n\n// loadRing attempts to load the keyring from the provided path.\n// Unsupported keys are ignored as long as at least a single valid key is\n// found.\nfunc loadRing(path string) (openpgp.EntityList, error) {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\tkeyring, err := openpgp.ReadKeyRing(f)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn keyring, nil\n}","sourceCodeStart":571,"sourceCodeEnd":607,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/pgp/keysource.go#L571-L607","documentation":"This error is returned by the pgp key-unlocking logic when it is asked to unlock a private key but no decryption mechanism applies: the key is neither encrypted with a passphrase it can prompt for nor otherwise decryptable. The library throws it because it cannot obtain the key material needed to use the key.","triggerScenarios":"Calling the key unlock function with a key whose PrivateKey is nil or of an unsupported type that matches none of the unlock branches (passphrase-encrypted, gpg-agent, etc.).","commonSituations":"Loading a PGP keyring file containing public-only or unsupported key types, passing the wrong key to a decrypt workflow, or keys created by newer OpenPGP implementations with algorithms this library does not handle.","solutions":["Verify the key being unlocked actually contains a private key entity (not just a public key).","Re-export the key from GPG including the private portion (gpg --export-secret-keys).","Ensure the key uses an algorithm/encryption format supported by this library (e.g. RSA, not a newer AEAD-only format).","If using passphrase-protected keys, make sure the passphrase branch is reachable (key is actually encrypted)."],"exampleFix":"// before\nkey, _ := openpgp.ReadArmoredKeyRing(pubOnlyArmoredKey)\nunlocked, err := unlock(key)\n// after\nif key.PrivateKey == nil {\n    return nil, fmt.Errorf(\"key has no private key material; export the secret key\")\n}\nunlocked, err := unlock(key)","handlingStrategy":"try-catch","validationCode":"if key == nil || key.PrivateKey == nil {\n    return fmt.Errorf(\"cannot unlock: key %s has no private key\", keyId)\n}","typeGuard":"func hasPrivateKey(e *openpgp.Entity) bool { return e != nil && e.PrivateKey != nil }","tryCatchPattern":"unlocked, err := unlockKey(key)\nif err != nil {\n    if err.Error() == \"no key to unlock\" {\n        return fmt.Errorf(\"key %s lacks unlockable private material: %w\", keyId, err)\n    }\n    return err\n}","preventionTips":["Export secret keys, not just public keys, when keys must be decrypted","Confirm key algorithm support before adding to the keyring","Test unlocking keys at setup time, not at first use"],"tags":["pgp","gpg","key-decryption","go"],"backgroundTag":"pgp-key-unlock-failed","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}