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.

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.

Appendix: 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)