golangci/golangci-lint · error

can't create output for %s: %w

Error message

can't create output for %s: %w

What it means

Printer.Print wraps any error returned by createWriter for the text output path. createWriter fails when it cannot MkdirAll the parent directory or cannot os.OpenFile the report file (O_CREATE|O_TRUNC|O_WRONLY, mode 0644); relative paths are resolved against basePath first.

Source

Thrown at pkg/printers/printer.go:76

		stdOut:     logutils.StdOut,
		stdErr:     logutils.StdErr,
	}, nil
}

// Print prints issues based on the formats defined.
//
//nolint:gocyclo,funlen // the complexity is related to the number of formats.
func (c *Printer) Print(issues []*result.Issue) error {
	if c.cfg.IsEmpty() {
		c.cfg.Text.Path = outputStdOut
	}

	var printers []issuePrinter

	if c.cfg.Text.Path != "" {
		w, closer, err := c.createWriter(&c.cfg.Text.SimpleFormat)
		if err != nil {
			return fmt.Errorf("can't create output for %s: %w", c.cfg.Text.Path, err)
		}

		defer closer()

		printers = append(printers, NewText(c.log, w, &c.cfg.Text))
	}

	if c.cfg.JSON.Path != "" {
		w, closer, err := c.createWriter(&c.cfg.JSON)
		if err != nil {
			return fmt.Errorf("can't create output for %s: %w", c.cfg.JSON.Path, err)
		}

		defer closer()

		printers = append(printers, NewJSON(w, c.reportData))
	}

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Check the wrapped os error: fix permissions (chmod/chown) or create the parent directory for the configured output path
  2. Point output.formats.text.path to a writable file (not a directory), or use 'stdout'/'stderr' to stream to the terminal
  3. In containers/CI, mount or set a writable output directory and pass an absolute path
  4. Verify basePath: relative paths are joined with basePath, so run from the expected working directory or use an absolute path

Example fix

// before (.golangci.yml)
output:
  formats:
    text:
      path: reports/
// after
output:
  formats:
    text:
      path: reports/lint.txt # must be a writable FILE path
Defensive patterns

Strategy: validation

Validate before calling

p := filepath.Join(basePath, cfg.Output.Formats.Text.Path)
if p != "stdout" && p != "stderr" {
	if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
		return fmt.Errorf("text output dir not writable: %w", err)
	}
	if fi, err := os.Stat(p); err == nil && fi.IsDir() {
		return fmt.Errorf("%s is a directory, expected a file", p)
	}
	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0o644)
	if err != nil {
		return fmt.Errorf("text output not writable: %w", err)
	}
	f.Close()
}

Try / catch

if err := p.Print(issues); err != nil {
	var perr *fs.PathError
	if errors.As(err, &perr) && strings.Contains(err.Error(), "can't create output") {
		log.Printf("report path %s unwritable (%v); falling back to stdout", perr.Path, perr.Err)
	}
}

Prevention

When it happens

Trigger: output.formats.text.path set to a file path that cannot be created: parent directory missing/unwritable, path is an existing directory, read-only filesystem, or permission denied; createWriter returns the os error which is wrapped here.

Common situations: CI containers with read-only workspaces, output paths pointing into nonexistent mount points, path is a directory (e.g. 'out/' or 'reports'), running as non-root user without write perms, Windows-invalid path characters, SELinux/AppArmor denials.

Related errors


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