getsops/sops · error
GnuPG binary error: %w
Error message
GnuPG binary error: %w
What it means
If the OpenPGP attempt fails, EncryptContext falls back to running the gpg binary via encryptWithGnuPG; a failure there is appended as 'GnuPG binary error: %w'. It means the external gpg executable itself failed (non-zero exit) when trying to encrypt the data key. Both errors are later aggregated into the 'could not encrypt data key with PGP key' error.
Source
Thrown at pgp/keysource.go:292
// fingerprint as the MasterKey.
func (key *MasterKey) EncryptContext(ctx context.Context, dataKey []byte) error {
var errs errSet
if !key.disableOpenPGP {
openpgpErr := key.encryptWithOpenPGP(dataKey)
if openpgpErr == nil {
log.WithField("fingerprint", key.Fingerprint).Info("Encryption succeeded")
return nil
}
errs = append(errs, fmt.Errorf("github.com/ProtonMail/go-crypto/openpgp error: %w", openpgpErr))
}
binaryErr := key.encryptWithGnuPG(ctx, dataKey)
if binaryErr == nil {
log.WithField("fingerprint", key.Fingerprint).Info("Encryption succeeded")
return nil
}
errs = append(errs, fmt.Errorf("GnuPG binary error: %w", binaryErr))
log.WithField("fingerprint", key.Fingerprint).Info("Encryption failed")
return fmt.Errorf("could not encrypt data key with PGP key: %w", errs)
}
// encryptWithOpenPGP attempts to encrypt the data key using OpenPGP with the
// PGP key that belongs to Fingerprint. It sets EncryptedDataKey, or returns
// an error.
func (key *MasterKey) encryptWithOpenPGP(dataKey []byte) error {
entity, err := key.retrievePubKey()
if err != nil {
return err
}
encBuf := new(bytes.Buffer)
armorBuf, err := armor.Encode(encBuf, "PGP MESSAGE", nil)
if err != nil {
return errView on GitHub (pinned to 13442bb981)
Solutions
- Check the wrapped cause for gpg's stderr (e.g. 'No public key', 'general error')
- Verify the binary: gpg --version, and fix SOPS_GPG_EXEC if set to a wrong path
- Import the recipient key: gpg --import <pubkey> and confirm with gpg --list-keys <fingerprint>
- If the key is expired/revoked, extend or replace it and update .sops.yaml
Example fix
// before export SOPS_GPG_EXEC=/usr/local/bin/gpg2 # binary does not exist // after unset SOPS_GPG_EXEC # or export SOPS_GPG_EXEC=$(command -v gpg)
Defensive patterns
Strategy: validation
Validate before calling
gpgExec := os.Getenv("SOPS_GPG_EXEC")
if gpgExec == "" { gpgExec = "gpg" }
if _, err := exec.LookPath(gpgExec); err != nil {
return fmt.Errorf("gpg binary %q not found: %w", gpgExec, err)
}
if out, err := exec.Command(gpgExec, "--version").CombinedOutput(); err != nil {
return fmt.Errorf("gpg not runnable: %v: %s", err, out)
} Try / catch
if err := key.EncryptContext(ctx, dataKey); err != nil {
if strings.Contains(err.Error(), "GnuPG binary error") {
// surface gpg stderr to the operator, do not retry blindly
log.Errorf("gpg fallback failed: %v", err)
}
return err
} Prevention
- Verify SOPS_GPG_EXEC points to an existing executable
- Install gpg in containers/images used for sops
- Import recipient keys into the binary keyring, not just files on disk
- Check key expiry: gpg --list-keys shows [expired] flags
When it happens
Trigger: Calling EncryptContext when the gpg binary is missing/unusable (bad SOPS_GPG_EXEC path), the GnuPG home is invalid, the key is absent from the binary keyring, or gpg exits with an error (e.g. 'No public key', unusable pubkey).
Common situations: SOPS_GPG_EXEC pointing to a nonexistent binary; gpg not installed in a container; key not imported for the current user; expired recipient key causing gpg to refuse encryption.
Related errors
- failed to encrypt sops data key with pgp: %s
- github.com/ProtonMail/go-crypto/openpgp error: %w
- could not encrypt data key with PGP key: %w
- Could not initialize AES GCM encryption cipher: %s
- Could not generate random bytes for IV: %s
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/3921811bc3d6c57f.
Report an issue: GitHub.