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
- Inspect the path: ls -ld "$GNUPGHOME" to confirm it is a directory
- If it is a file, move it aside and create a directory: mkdir -p <path> && chmod 700 <path>
- Correct the GNUPGHOME environment variable or config value to point at the directory, not a keyring file
- 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
- Point GNUPGHOME at a directory, never at a keyring file like keys.gpg
- Validate GnuPGHome before calling Import/ApplyToMasterKey
- Use NewGnuPGHome() (which creates a real temp dir) instead of hand-built paths
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
- failed to create new GnuPG home: %w
- cannot read armored key data from file: %w
- GNUPGHOME does not exist
- cannot stat GNUPGHOME: %w
- failed to construct Azure Key Vault client to decrypt data:
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/43bfb16dce14da2c.
Report an issue: GitHub.