getsops/sops · error

cannot stat GNUPGHOME: %w

Error message

cannot stat GNUPGHOME: %w

What it means

GnuPGHome.Validate() calls os.Lstat on the GNUPGHOME path and wraps any stat error that is not ENOENT with this message. It means the path exists per some earlier check but cannot be stat'd — typically a permissions problem on a parent directory, a dangling symlink, or an I/O error. The library throws it because sops needs to verify the GnuPG home directory and its 0700 permissions before importing keys or running gpg.

Source

Thrown at pgp/keysource.go:203

	}
	return os.RemoveAll(d.String())
}

// 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) {

View on GitHub (pinned to 13442bb981)

Solutions

  1. Check the wrapped cause (%w) with ls -la on the path and each parent directory
  2. Fix permissions so the invoking user can traverse all parent directories (chmod +x on parents)
  3. Replace dangling symlinks or recreate the GNUPGHOME directory with mkdir -p and chmod 700
  4. Avoid pointing GNUPGHOME at another user's directory; use your own or a shared service account

Example fix

// before
export GNUPGHOME=/root/.gnupg  # run as non-root user
// after
export GNUPGHOME=$HOME/.gnupg && mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME"
Defensive patterns

Strategy: validation

Validate before calling

home := pgp.GnuPGHome(os.Getenv("GNUPGHOME"))
if err := home.Validate(); err != nil {
    return fmt.Errorf("GNUPGHOME unusable: %w", err)
}
// also verify parent traversal:
for dir := filepath.Dir(string(home)); dir != "/"; dir = filepath.Dir(dir) {
    if fi, err := os.Stat(dir); err != nil || fi.Mode().Perm()&0o044 == 0 {
        return fmt.Errorf("cannot traverse %s: %v", dir, err)
    }
}

Type guard

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

Prevention

When it happens

Trigger: Calling ImportContext, Cleanup, or ApplyToMasterKey with a GnuPGHome whose path cannot be lstat'ed for a reason other than non-existence (e.g. permission denied on a parent directory, too-long path, dangling symlink).

Common situations: GNUPGHOME set to a path inside a directory the current user cannot read; a symlink pointing nowhere; NFS/EACCES issues; using another user's home directory without read access on intermediate dirs.

Related errors


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