golangci/golangci-lint · error

formatting %s: %w

Error message

formatting %s: %w

What it means

After printing the transformed AST, writeNewFile runs the result through golang.org/x/tools/imports.Process, which parses, formats, and fixes up import statements. The 'formatting %s: %w' wrap means the printed AST text is not valid Go or the imports fix-up failed. The most common cause: after processFile drops const/var declarations and rewrites the package name, referenced imports or identifiers disappear, leaving invalid or unused-import state that Process cannot resolve into compilable source.

Source

Thrown at pkg/commands/internal/migrate/cloner/cloner.go:185

}

func writeNewFile(fset *token.FileSet, file *ast.File, srcPath, dstDir string) error {
	var buf bytes.Buffer

	buf.WriteString("// Code generated by pkg/commands/internal/migrate/cloner/cloner.go. DO NOT EDIT.\n\n")

	err := printer.Fprint(&buf, fset, file)
	if err != nil {
		return fmt.Errorf("printing %s: %w", srcPath, err)
	}

	dstPath := filepath.Join(dstDir, filepath.Base(srcPath))

	_ = os.MkdirAll(filepath.Dir(dstPath), os.ModePerm)

	formatted, err := imports.Process(dstPath, buf.Bytes(), nil)
	if err != nil {
		return fmt.Errorf("formatting %s: %w", dstPath, err)
	}

	//nolint:gosec,mnd // The permission is right.
	err = os.WriteFile(dstPath, formatted, 0o644)
	if err != nil {
		return fmt.Errorf("writing file %s: %w", dstPath, err)
	}

	return nil
}

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Read the wrapped imports.Process error — it reports the parse error location in the generated file
  2. Inspect the corresponding generated file under pkg/commands/internal/migrate/versiontwo to see the invalid source
  3. Check whether the source struct fields reference types/values that processFile strips (const/var declarations) and adjust processStructFields/convertType to keep or convert them
  4. Re-run the cloner after fixing pkg/config or the transformation rules

Example fix

// before (field type defined in a stripped const/var block)
type Config struct {
    Mode config.Mode // type dropped by processFile -> unresolvable
}
// after: keep type declarations or convert the field
type Config struct {
    Mode *string
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-check: attempt imports.Process on a copy before writing to disk
out, err := imports.Process(dstPath, buf.Bytes(), nil)
if err != nil {
    log.Printf("generated source for %s is invalid: %v\n--- source ---\n%s", dstPath, err, buf.String())
    return err
}

Try / catch

if err := writeNewFile(fset, file, srcPath, dstDir); err != nil {
    if strings.Contains(err.Error(), "formatting ") {
        log.Printf("imports.Process failed for %s — inspect generated source under %s", srcPath, dstDir)
    }
    return err
}

Prevention

When it happens

Trigger: writeNewFile is called and imports.Process(dstPath, buf.Bytes(), nil) fails because the generated versiontwo package source does not parse — e.g. struct fields reference types that were removed with the const/var declarations, or the rewritten code contains references to deleted symbols.

Common situations: Adding new struct types to pkg/config whose fields use types defined in removed const/var blocks; renaming the package to versiontwo while code still references the old package-qualified identifiers; unresolvable imports in the copied file.

Related errors


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