getsops/sops · error

failed to encrypt sops data key with pgp: %s

Error message

failed to encrypt sops data key with pgp: %s

What it means

encryptWithGnuPG runs the gpg binary with --encrypt and --trusted-key; if gpg exits non-zero, this error is returned with gpg's trimmed stderr embedded. It identifies the exact failure output from the gpg process used to encrypt the sops data key. Notably it uses --no-encrypt-to and trusts the key explicitly, so most failures are key availability/usability problems.

Source

Thrown at pgp/keysource.go:357

// PGP key that belongs to Fingerprint. It sets EncryptedDataKey, or returns
// an error.
func (key *MasterKey) encryptWithGnuPG(ctx context.Context, dataKey []byte) error {
	fingerprint := shortenFingerprint(key.Fingerprint)

	args := []string{
		"--no-default-recipient",
		"--yes",
		"--encrypt",
		"-a",
		"-r",
		key.Fingerprint,
		"--trusted-key",
		fingerprint,
		"--no-encrypt-to",
	}
	stdout, stderr, err := gpgExec(ctx, key.gnuPGHomeDir, args, bytes.NewReader(dataKey))
	if err != nil {
		return fmt.Errorf("failed to encrypt sops data key with pgp: %s", strings.TrimSpace(stderr.String()))
	}

	key.SetEncryptedDataKey(bytes.TrimSpace(stdout.Bytes()))
	return nil
}

// EncryptIfNeeded encrypts the data key with PGP only if it's needed,
// that is, if it hasn't been encrypted already.
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(dataKey)
	}
	return nil
}

// EncryptedDataKey returns the encrypted data key this master key holds.
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)

View on GitHub (pinned to 13442bb981)

Solutions

  1. Read the embedded stderr string for gpg's specific message
  2. gpg --import the recipient public key and verify with gpg --list-keys <fingerprint>
  3. Use --import-options show-only or set the key to ultimate trust if 'No public key'/'unusable pubkey' persists; replace expired keys
  4. Confirm gpg is installed and SOPS_GPG_EXEC resolves correctly

Example fix

// before
sops -e file.yaml  # failed to encrypt sops data key with pgp: gpg: No public key
// after
gpg --recv-keys <fingerprint> && sops -e file.yaml
Defensive patterns

Strategy: try-catch

Validate before calling

if out, err := exec.Command("gpg", "--homedir", gnupgHome, "--list-keys", fingerprint).CombinedOutput(); err != nil {
    return fmt.Errorf("recipient %s missing from keyring: %s", fingerprint, out)
}

Try / catch

if err := encrypt(...); err != nil {
    var gpgErr *fmt.Errorf
    if errors.As(err, &gpgErr) && strings.Contains(err.Error(), "failed to encrypt sops data key with pgp") {
        // err text contains gpg's trimmed stderr; log it verbatim for diagnosis
        log.Errorf("gpg encrypt failed: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: EncryptContext falls back to encryptWithGnuPG and gpg fails: key not in keyring ('No public key'), key expired/revoked, gpg binary missing, GNUPGHOME invalid, or agent/PIN issues.

Common situations: Recipient key never imported; running as a different user with another GNUPGHOME; expired key in .sops.yaml; gpg 2.x prompting in a non-interactive CI environment.

Related errors


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/ea6b21dcad8716f1. Report an issue: GitHub.