getsops/sops · error

Failed to read %q: %w

Error message

Failed to read %q: %w

What it means

decrypt.File is a convenience wrapper that reads a local encrypted file with os.ReadFile before handing the bytes to DataWithFormat. This error wraps the os.ReadFile failure, meaning the encrypted input file could not be opened or read — sops never got to any decryption step.

Source

Thrown at decrypt/decrypt.go:24

import (
	"fmt"
	"os"
	"time"

	"github.com/getsops/sops/v3/aes"
	"github.com/getsops/sops/v3/cmd/sops/common"
	. "github.com/getsops/sops/v3/cmd/sops/formats" // Re-export
	"github.com/getsops/sops/v3/config"
)

// File is a wrapper around Data that reads a local encrypted
// file and returns its cleartext data in an []byte
func File(path, format string) (cleartext []byte, err error) {
	// Read the file into an []byte
	encryptedData, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("Failed to read %q: %w", path, err)
	}

	// uses same logic as cli.
	formatFmt := FormatForPathOrString(path, format)
	return DataWithFormat(encryptedData, formatFmt)
}

// DataWithFormat is a helper that takes encrypted data, and a format enum value,
// decrypts the data and returns its cleartext in an []byte.
func DataWithFormat(data []byte, format Format) (cleartext []byte, err error) {

	store := common.StoreForFormat(format, config.NewStoresConfig())

	// Load SOPS file and access the data key
	tree, err := store.LoadEncryptedFile(data)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 13442bb981)

Solutions

  1. Verify the path exists and is a file: os.Stat / ls -la before calling decrypt.File
  2. Convert to an absolute path (filepath.Abs) so the call is independent of the working directory
  3. Check read permissions for the running user (chmod/chown or run with correct uid)
  4. If the data is remote (S3/GCS), fetch it yourself first and pass the bytes to decrypt.Data instead

Example fix

// before
cleartext, err := decrypt.File("secrets.enc.yaml", "yaml")
// after
abs, _ := filepath.Abs("secrets.enc.yaml")
if _, err := os.Stat(abs); err != nil {
    return fmt.Errorf("encrypted file missing: %w", err)
}
cleartext, err := decrypt.File(abs, "yaml")
Defensive patterns

Strategy: try-catch

Validate before calling

abs, err := filepath.Abs(path)
if err != nil {
    return err
}
info, err := os.Stat(abs)
if err != nil {
    return fmt.Errorf("encrypted file not readable: %w", err)
}
if info.IsDir() {
    return fmt.Errorf("%s is a directory", abs)
}

Try / catch

cleartext, err := decrypt.File(path, format)
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) && os.IsNotExist(perr) {
        return fmt.Errorf("encrypted input %q does not exist", path)
    }
    return err
}

Prevention

When it happens

Trigger: Calling decrypt.File(path, format) with a path that doesn't exist, points to a directory, or is unreadable by the current user; also when the process lacks filesystem permissions (e.g. restricted container).

Common situations: Wrong relative path after changing working directory; file deleted between write and decrypt in CI; secret file mounted with wrong permissions in Kubernetes/Docker; passing an sops URL or S3 URI where a local path is required.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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