getsops/sops · error

cannot import armored key data into GnuPG keyring: %w

Error message

cannot import armored key data into GnuPG keyring: %w

What it means

GnuPGHome.ImportContext imports armored key bytes by running `gpg --batch --import` with the home directory set. Before doing so it validates the home path; a failed Validate is wrapped with this message. After validation, a failing gpg execution also produces an import error (the follow-on branch reads stderr), but this exact wrap site fires when the GnuPGHome path itself is invalid.

Source

Thrown at pgp/keysource.go:144

	}
	return GnuPGHome(tmpDir), nil
}

// Import attempts to import the armored key bytes into the GnuPGHome keyring.
// It returns an error if the GnuPGHome does not pass Validate, or if the
// import failed.
//
// Consider using ImportContext instead.
func (d GnuPGHome) Import(armoredKey []byte) error {
	return d.ImportContext(context.Background(), armoredKey)
}

// ImportContext attempts to import the armored key bytes into the GnuPGHome keyring.
// It returns an error if the GnuPGHome does not pass Validate, or if the
// import failed.
func (d GnuPGHome) ImportContext(ctx context.Context, armoredKey []byte) error {
	if err := d.Validate(); err != nil {
		return fmt.Errorf("cannot import armored key data into GnuPG keyring: %w", err)
	}

	args := []string{"--batch", "--import"}
	_, stderr, err := gpgExec(ctx, d.String(), args, bytes.NewReader(armoredKey))
	if err != nil {
		stderrStr := strings.TrimSpace(stderr.String())
		errStr := err.Error()
		var sb strings.Builder
		sb.WriteString("failed to import armored key data into GnuPG keyring")
		if len(stderrStr) > 0 {
			if len(errStr) > 0 {
				fmt.Fprintf(&sb, " (%s)", errStr)
			}
			fmt.Fprintf(&sb, ": %s", stderrStr)
		} else if len(errStr) > 0 {
			fmt.Fprintf(&sb, ": %s", errStr)
		}
		return errors.New(sb.String())

View on GitHub (pinned to 13442bb981)

Solutions

  1. Always obtain the GnuPGHome from NewGnuPGHome() and check its error instead of constructing it manually.
  2. Check the wrapped cause: 'empty GNUPGHOME path', 'must be an absolute path', or 'does not exist', and fix accordingly (initialize or use an absolute existing directory).
  3. Ensure the home is not cleaned up (Cleanup) before all imports complete.
  4. Verify the gpg binary exists if validation passes but the import still fails; the wrapped error will show gpg stderr.

Example fix

// before
var home pgp.GnuPGHome
home.Import(pubKey) // empty path
// after
home, err := pgp.NewGnuPGHome()
if err != nil { return err }
if err := home.Import(pubKey); err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

// Go
func importSafe(home pgp.GnuPGHome, key []byte) error {
  if err := home.Validate(); err != nil { return err }
  return home.Import(key)
}

Type guard

func initialized(home pgp.GnuPGHome) bool { return home != "" && filepath.IsAbs(home.String()) }

Try / catch

// Go
if err := home.Import(armoredKey); err != nil {
  if strings.Contains(err.Error(), "cannot import armored key data into GnuPG keyring") {
    // recreate home and retry once
    home, rerr = pgp.NewGnuPGHome(); if rerr != nil { return rerr }
    return home.Import(armoredKey)
  }
  return err
}

Prevention

When it happens

Trigger: Calling Import (or ImportContext) on a zero-valued GnuPGHome (""), a relative path, a deleted temp dir, or a path that is not a directory - i.e. d.Validate() returns an error before gpg runs.

Common situations: Constructing GnuPGHome{} directly instead of via NewGnuPGHome; calling Cleanup earlier and then reusing the (deleted) home; using a relative path like "gnupg-home"; tests importing into a home that was never initialized.

Related errors


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