golangci/golangci-lint · error

write file %s: %w

Error message

write file %s: %w

What it means

After regenerating plugins.go source, updatePluginsFile writes it back with os.WriteFile preserving the original file mode captured by the earlier Stat. If the write fails (permissions, read-only filesystem, disk full), the error wraps the cause as 'write file <path>: ...'.

Source

Thrown at pkg/commands/internal/imports.go:39

func (b Builder) updatePluginsFile() error {
	importsDest := filepath.Join(b.repo, "cmd", "golangci-lint", "plugins.go")

	info, err := os.Stat(importsDest)
	if err != nil {
		return fmt.Errorf("file %s not found: %w", importsDest, err)
	}

	source, err := generateImports(b.cfg)
	if err != nil {
		return fmt.Errorf("generate imports: %w", err)
	}

	b.log.Infof("generated imports info %s:\n%s\n", importsDest, source)

	err = os.WriteFile(filepath.Clean(importsDest), source, info.Mode())
	if err != nil {
		return fmt.Errorf("write file %s: %w", importsDest, err)
	}

	return nil
}

func generateImports(cfg *Configuration) ([]byte, error) {
	impTmpl, err := template.New("plugins.go").Parse(importsTemplate)
	if err != nil {
		return nil, fmt.Errorf("parse template: %w", err)
	}

	var imps []string
	for _, plugin := range cfg.Plugins {
		imps = append(imps, plugin.Import)
	}

	buf := &bytes.Buffer{}

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Make cmd/golangci-lint/plugins.go writable (chmod u+w or chown to the build user)
  2. Run the build outside read-only mounts or copy the repo to a writable location first
  3. Fix container UID/ownership so the build user can write the checkout
  4. Check disk space and that cmd/golangci-lint/ still exists

Example fix

// before
$ golangci-lint custom   # fails: write file ...: permission denied
// after
$ chmod u+w cmd/golangci-lint/plugins.go && golangci-lint custom
Defensive patterns

Strategy: validation

Validate before calling

p := filepath.Join(repo, "cmd", "golangci-lint", "plugins.go")
if info, err := os.Stat(p); err == nil && info.Mode().Perm()&0o200 == 0 {
	os.Chmod(p, info.Mode()|0o200)
}

Try / catch

err := builder.Build(ctx)
if err != nil && strings.Contains(err.Error(), "write file "+importsDest) {
	var pe *fs.PathError
	if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrPermission) {
		os.Chmod(importsDest, 0o644) // fix and retry
	}
}

Prevention

When it happens

Trigger: os.WriteFile(importsDest) fails during Build — typically EACCES on cmd/golangci-lint/plugins.go, a read-only checkout, or the directory removed between Stat and Write.

Common situations: Building inside a container with a read-only mounted repo; file owned by root or another UID; disk full; generated file made immutable.

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/60cd58d1b16933c6. Report an issue: GitHub.