getsops/sops · error

GNUGPHOME is not a directory

Error message

GNUGPHOME is not a directory

What it means

GnuPGHome.Validate() checks fi.IsDir() after Lstat; if the path is a regular file, socket, or symlink target that is not a directory, it returns this error (note the typo 'GNUGPHOME'). The library requires GNUPGHOME to be an actual directory because gpg keyrings and sops key imports live inside it.

Source

Thrown at pgp/keysource.go:206

// Validate ensures the GnuPGHome is a valid GnuPG home directory path.
// When validation fails, it returns a descriptive reason as error.
func (d GnuPGHome) Validate() error {
	if d == "" {
		return fmt.Errorf("empty GNUPGHOME path")
	}
	if !filepath.IsAbs(d.String()) {
		return fmt.Errorf("GNUPGHOME must be an absolute path")
	}
	fi, err := os.Lstat(d.String())
	if err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("GNUPGHOME does not exist")
		}
		return fmt.Errorf("cannot stat GNUPGHOME: %w", err)
	}
	if !fi.IsDir() {
		return fmt.Errorf("GNUGPHOME is not a directory")
	}
	if perm := fi.Mode().Perm(); perm != 0o700 {
		return fmt.Errorf("GNUPGHOME has invalid permissions: got %#o wanted %#o", perm, 0o700)
	}
	return nil
}

// String returns the GnuPGHome as a string. It does not Validate.
func (d GnuPGHome) String() string {
	return string(d)
}

// ApplyToMasterKey configures the GnuPGHome on the provided key if it passes
// Validate.
func (d GnuPGHome) ApplyToMasterKey(key *MasterKey) {
	if err := d.Validate(); err == nil {
		key.gnuPGHomeDir = d.String()
	}

View on GitHub (pinned to 13442bb981)

Solutions

  1. Inspect the path: ls -ld "$GNUPGHOME" to confirm it is a directory
  2. If it is a file, move it aside and create a directory: mkdir -p <path> && chmod 700 <path>
  3. Correct the GNUPGHOME environment variable or config value to point at the directory, not a keyring file
  4. Call Validate() on the GnuPGHome early in your program to fail fast with a clear message

Example fix

// before
home := pgp.GnuPGHome("/home/me/keys.gpg") // a file
// after
home := pgp.GnuPGHome("/home/me/.gnupg") // a directory, chmod 700
Defensive patterns

Strategy: validation

Validate before calling

home := pgp.GnuPGHome(cfg.GnuPGHome)
if err := home.Validate(); err != nil {
    return fmt.Errorf("check GNUPGHOME=%q: %w", cfg.GnuPGHome, err)
}

Type guard

func isGnuPGDir(h pgp.GnuPGHome) bool {
    fi, err := os.Stat(string(h))
    return err == nil && fi.IsDir()
}

Prevention

When it happens

Trigger: Calling ImportContext, Cleanup, or ApplyToMasterKey with a GnuPGHome string that points to a non-directory file (e.g. a keyring file, or NewGnuPGHome temp dir that was replaced by a file).

Common situations: GNUPGHOME accidentally set to a .gpg keyring file path; a cleanup routine replaced the temp directory with a file; typo in config pointing at a file instead of a directory.

Related errors


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