Skip to content

fix: make vet accept %w in Errorf, document Wrap(nil) contract#32

Merged
ernado merged 2 commits into
mainfrom
fix/errorf-vet-w
Jul 24, 2026
Merged

fix: make vet accept %w in Errorf, document Wrap(nil) contract#32
ernado merged 2 commits into
mainfrom
fix/errorf-vet-w

Conversation

@ernado

@ernado ernado commented Jul 24, 2026

Copy link
Copy Markdown
Member

What

Two related hardening changes around the library's core contracts:

  1. Document and pin the Wrap(nil) contract. Wrap/Wrapf always return a non-nil error, even for a nil input — matching fmt.Errorf with a nil %w operand, and unlike pkg/errors. This was deliberate but undocumented. Now stated in godoc and README (with the guarded if err != nil idiom), and pinned by TestWrapNil so it cannot change silently. Changing it was considered and rejected: it would silently alter control flow in every downstream module, and cmd/gowrapper's ErrorfWrap rewrite is only semantics-preserving because both sides agree on nil.

  2. Fix the vet false positive on Errorf with %w. Errorf genuinely supports %w by falling back to fmt.Errorf (asserted by TestFormatError), but newer toolchains failed go test ./... with errors.Errorf does not support error-wrapping directive %w. Vet classifies printf wrappers by the calls that forward (format, a...); the fmt.Sprintf forward on the traced path downgraded Errorf from errorf-like to printf-like. Moving the Sprintf call behind a non-variadic helper leaves fmt.Errorf as the only forward, so vet now classifies Errorf correctly. No behavior change, and the same false positive disappears for downstream users writing errors.Errorf("...: %w", err).

Test plan

  • go vet ./... — clean (previously failed on format_test.go:488)
  • go test ./... — passes with vet enabled, including new TestWrapNil

ernado added 2 commits July 24, 2026 19:10
Wrap and Wrapf always return a non-nil error even for a nil input,
matching fmt.Errorf with a nil %w operand (and unlike pkg/errors).
This is deliberate: gowrapper's Errorf-to-Wrap rewrite is only
semantics-preserving because both sides agree on nil.

Document the contract in godoc and README, and add TestWrapNil so it
cannot change silently.
Errorf falls back to fmt.Errorf when the format contains %w, but vet's
printf analyzer flagged callers with 'does not support error-wrapping
directive %w'. The analyzer classifies a wrapper by the calls that
forward (format, a...): the fmt.Sprintf forward on the traced path
downgraded Errorf from errorf-like to printf-like.

Move the Sprintf call behind a non-variadic helper so the only printf
forward left is fmt.Errorf. No behavior change; 'go test ./...' now
passes with vet enabled.
@ernado
ernado merged commit bb1482a into main Jul 24, 2026
@ernado
ernado deleted the fix/errorf-vet-w branch July 24, 2026 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant