golangci/golangci-lint · error

file %s open: %w

Error message

file %s open: %w

What it means

After findConfigurationFile succeeds, LoadConfiguration opens the located file with os.Open; this wrapper reports 'file %s open'. The config path was found in the directory listing but could not be opened for reading, so the wrapped error is a *os.PathError (usually EACCES or a race where the file disappeared).

Source

Thrown at pkg/commands/internal/configuration.go:103

	// Version of the module.
	// Only for module available through a Go proxy.
	Version string `yaml:"version,omitempty"`

	// Path to the local module.
	// Only for local module.
	Path string `yaml:"path,omitempty"`
}

func LoadConfiguration() (*Configuration, error) {
	configFilePath, err := findConfigurationFile()
	if err != nil {
		return nil, fmt.Errorf("file %s not found: %w", configFilePath, err)
	}

	file, err := os.Open(configFilePath)
	if err != nil {
		return nil, fmt.Errorf("file %s open: %w", configFilePath, err)
	}

	defer func() { _ = file.Close() }()

	var cfg Configuration

	err = yaml.NewDecoder(file).Decode(&cfg)
	if err != nil {
		return nil, fmt.Errorf("YAML decoding: %w", err)
	}

	return &cfg, nil
}

func findConfigurationFile() (string, error) {
	entries, err := os.ReadDir(".")
	if err != nil {
		return "", fmt.Errorf("read directory: %w", err)

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Check and fix permissions: chmod +r <configFile> (and ensure parent directories allow traversal)
  2. Confirm the matched name is a regular readable file, not a directory or broken symlink (ls -la)
  3. Re-run if a concurrent process may have removed the file between listing and open
  4. If permissions cannot be relaxed, run the command as a user with read access

Example fix

// before
-rw------- 1 ci ci config.yaml   # CI user cannot read
// after
chmod 644 config.yaml
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(configFilePath); err != nil {
    return fmt.Errorf("config %s unreadable: %w", configFilePath, err)
} else if !fi.Mode().IsRegular() {
    return fmt.Errorf("config %s is not a regular file", configFilePath)
} else if fi.Mode().Perm()&0o400 == 0 {
    return fmt.Errorf("config %s has no read permission for current user", configFilePath)
}

Try / catch

cfg, err := LoadConfiguration()
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) && errors.Is(perr, fs.ErrPermission) {
        log.Fatalf("config %s: permission denied — chmod +r or run as a user with access", perr.Path)
    }
    return err
}

Prevention

When it happens

Trigger: findConfigurationFile matched the filename but os.Open(configFilePath) failed: no read permission on the file, it is a directory or symlink to something unreadable, or it was deleted between listing and opening.

Common situations: Config file committed with 000/600 permissions while CI runs as another user, config checked out with restrictive umask, a directory named like the config file, or a broken symlink named config.yaml.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02). Data as JSON: /api/errors/845dc8ba7670296b. Report an issue: GitHub.