golangci/golangci-lint · error

open file: %w

Error message

open file: %w

What it means

fakeloader.Load wraps the error from os.Open(srcPath) when it cannot open the configuration file for reading. This loader preserves original key casing (unlike Viper, which lowercases keys), so it is used to re-read the config during migration and tests. The error means the source configuration file could not be opened at all.

Source

Thrown at pkg/commands/internal/migrate/fakeloader/fakeloader.go:18

package fakeloader

import (
	"fmt"
	"os"

	"github.com/go-viper/mapstructure/v2"

	"github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/parser"
	"github.com/golangci/golangci-lint/v2/pkg/config"
)

// Load is used to keep case of configuration.
// Viper serialize raw map keys in lowercase, this is a problem with the configuration of some linters.
func Load(srcPath string, old any) error {
	file, err := os.Open(srcPath)
	if err != nil {
		return fmt.Errorf("open file: %w", err)
	}

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

	raw := map[string]any{}

	err = parser.Decode(file, raw)
	if err != nil {
		return err
	}

	// NOTE: this is inspired by viper internals.
	cc := &mapstructure.DecoderConfig{
		Result:           old,
		WeaklyTypedInput: true,
		DecodeHook:       config.DecodeHookFunc(),
	}

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Verify the configuration file path passed via --config or the default (.golangci.yml) exists: ls -l <path>.
  2. Check read permissions on the file and its parent directories.
  3. If running in CI/containers, ensure the config file is mounted/copied into the workspace.
  4. Confirm you are running the command from the directory where the config resides.

Example fix

// before
loader.Load(".golangci.yaml") // file is actually .golangci.yml
// after
if _, err := os.Stat(".golangci.yml"); err != nil {
    log.Fatal("config file not found: ", err)
}
loader.Load(".golangci.yml")
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(cfgPath)
if err != nil { /* file missing */ }
if info.IsDir() { /* path is a directory */ }

Try / catch

if err := loader.Load(srcPath, &old); err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        // fall back to default config or abort with friendly message
    }
    return err
}

Prevention

When it happens

Trigger: Calling Load with a srcPath that does not exist, points to a directory, or lacks read permissions; called from testFile and persistentPreRunE during `golangci-lint migrate`.

Common situations: Typo in the --config path, config file deleted or moved before migrating, running in a container where the config path was not mounted, or insufficient file permissions.

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 golangci/golangci-lint@ed7a235d2d (2026-09-02). Data as JSON: /api/errors/9d4eb4b89fe55d78. Report an issue: GitHub.