Go vet: Printf format %d has arg of wrong type
Fix go vet printf warnings when format verbs do not match argument types, and understand why fmt still prints apparently correct output.
Symptoms
- printf: fmt.Sprintf call has arguments but no formatting directives
- Printf format %d has arg x of wrong type string
- go vet fails CI even though the program prints plausible output
Possible causes
- A format verb does not match the argument type (%d with a string)
- A custom type has no String() method, so %s prints a struct
- A recursive Printf call passes a format string built from user input
- Arguments are missing or extra relative to the verbs
Diagnosis steps
Read the exact vet message and the argument type
Interpretation
vet names the format, the verb, and the Go type of the argument, which pinpoints the mismatch.
Compare verbs to argument order
Interpretation
A shifted argument list often makes every subsequent verb wrong, not just one.
Run vet across the module
go vet ./...Interpretation
vet also flags Printf-like wrappers, catching helpers that forward formats unsafely.
Root cause
fmt uses reflection at run time and will happily print a mis-typed argument with placeholder text such as %!d(string=abc), so tests may pass while output is wrong. go vet performs static analysis of format strings and argument types and reports the mismatch before it reaches users.
Fix
Match each verb to its argument type (%d integers, %s strings, %v any, %q quoted). Add a String() method to custom types you print with %s. Ensure wrapper functions that build format strings are marked, and never construct a format string from untrusted input — pass it as a value with %v instead.
Verify the fix
Run `go vet ./...`; it reports no printf issues and CI passes, while a test asserting the formatted string confirms the output is now correct.
Symptoms
go vet fails with a message like Printf format %d has arg x of wrong type string, yet the program appears to print sensible output at run time.
Diagnosis
Read the verb, the argument type, and the call site together:
count := "5"
log.Printf("count=%d", count) // vet: %d with string
Check argument order too — one shifted value misaligns every following verb.
Fix
Align verbs and types, and implement String() for custom types:
log.Printf("count=%s", count) // string
type Money struct{ Cents int }
func (m Money) String() string { return fmt.Sprintf("%.2f", float64(m.Cents)/100) }
log.Printf("total=%s", Money{1250})
Verification
go vet ./... passes and a unit test asserting the exact formatted output confirms the fix, since fmt would otherwise emit a %!d placeholder without erroring.
Additional Notes
Printf-style faults are dangerous precisely because they rarely crash: the formatter substitutes a placeholder and the program keeps running, so a log line quietly records the wrong value. Static analysis catches these before runtime, but only for format strings it can see literally, which is why wrappers that assemble formats dynamically deserve extra tests. Reviewing argument order whenever a message is edited is worthwhile, because inserting one value can misalign every verb after it and turn an otherwise correct string into misleading output.