golangci/golangci-lint · error

printing %s: %w

Error message

printing %s: %w

What it means

writeNewFile serializes the transformed AST back to text with printer.Fprint. The 'printing %s: %w' wrap means the go/printer failed to emit the AST. This is rare — go/printer only errors on internal problems like an AST node with invalid positions or malformed comments — and typically points to a bug in the AST mutation done by processFile/processStructFields.

Source

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

	if key == ",squash" {
		return wrapStructTag(`yaml:",inline"`)
	}

	return wrapStructTag(fmt.Sprintf(`yaml:"%[1]s,omitempty" toml:"%[1]s,multiline,omitempty"`, key))
}

func wrapStructTag(s string) string {
	return "`" + s + "`"
}

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. Look at the wrapped printer error to identify which node/position is invalid
  2. Verify any hand-constructed ast nodes (e.g. StarExpr in convertType) reuse positions from the original expression rather than token.NoPos where possible
  3. Run gofmt on the source file to rule out unusual formatting/comment edge cases
  4. Simplify the AST mutation, or re-derive nodes with ast.NewIdent / copied positions, then re-run the cloner

Example fix

// before
return &ast.StarExpr{X: ident}
// after (preserve source position so the printer can emit valid output)
return &ast.StarExpr{Star: ident.Pos(), X: ident}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check: print the mutated AST to a buffer and gofmt-validate before writing
var pre bytes.Buffer
if err := printer.Fprint(&pre, fset, file); err != nil {
    return fmt.Errorf("printing %s: %w", srcPath, err)
}
if _, err := format.Source(pre.Bytes()); err != nil {
    return fmt.Errorf("printed AST invalid for %s: %w", srcPath, err)
}

Try / catch

if err := writeNewFile(fset, file, srcPath, dstDir); err != nil {
    if strings.Contains(err.Error(), "printing ") {
        log.Printf("printer failed for %s: %v — inspect AST mutations", srcPath, err)
    }
    return err
}

Prevention

When it happens

Trigger: writeNewFile is called (from the processPackage walk callback) after processFile mutated the AST, and printer.Fprint returns an error, usually because hand-constructed or mutated AST nodes carry inconsistent position info (e.g. a StarExpr wrapping an ident whose Pos is invalid after field manipulation).

Common situations: Extending the cloner's AST transformations (convertType, processStructFields) with manually built nodes lacking valid positions; corrupted comment positions in the source file can also trip the printer.

Related errors


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