Go interface conversion panic — Wrong Dynamic Type
Fix "interface conversion: interface {} is X, not Y" by using the two-value type assertion or a type switch instead of a single-value cast.
Symptoms
- panic: interface conversion: interface {} is string, not int
- The panic names two concrete types you did not expect
- The failure appears only for certain JSON or map inputs
Possible causes
- A single-value assertion v := x.(T) is used where x may hold another dynamic type
- JSON numbers decode as float64, so asserting to int panics
- A map value stored as one type is read assuming another
Diagnosis steps
Reproduce and read the panic types
go run - <<'EOF'
package main
import "fmt"
func main(){ var v any = "7"; n := v.(int); fmt.Println(n) }
EOFInterpretation
The panic message gives the actual and asserted types, so you can correct the assertion or input.
Switch to the comma-ok form
n, ok := v.(int); if !ok { /* handle */ }Interpretation
The two-value assertion never panics and lets you branch on the missing type instead of crashing.
Root cause
A single-value type assertion x.(T) panics when the dynamic type of x is not exactly T. JSON and interface-heavy code frequently store unexpected concrete types, so the unsafe form is a latent runtime crash.
Fix
Use the comma-ok assertion to check before use, or a type switch to handle each concrete type (remembering JSON numbers are float64), and validate at the boundary where untyped data enters.
Risk note
Converting JSON numbers straight to int truncates; decode into a typed struct with json.Decoder.UseNumber when precision matters.
Verify the fix
Rerun with the mismatched input and confirm the guard branch is taken instead of a panic. Add table tests for string, int, and float64 inputs.
Symptoms
When you hit “Go interface conversion panic — Wrong Dynamic Type”, guessing wastes time. The guide below follows evidence: reproduce the symptom, run targeted diagnostics, identify the root cause, apply the fix, and verify it with concrete checks.
Observed symptoms include:
- panic: interface conversion: interface {} is string, not int
- The panic names two concrete types you did not expect
- The failure appears only for certain JSON or map inputs
Reproduce and Diagnose
Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.
Step 1: Reproduce and read the panic types
go run - <<'EOF'
package main
import "fmt"
func main(){ var v any = "7"; n := v.(int); fmt.Println(n) }
EOF
The panic message gives the actual and asserted types, so you can correct the assertion or input.
Step 2: Switch to the comma-ok form
n, ok := v.(int); if !ok { /* handle */ }
The two-value assertion never panics and lets you branch on the missing type instead of crashing.
Likely contributors, in order of frequency:
- A single-value assertion v := x.(T) is used where x may hold another dynamic type
- JSON numbers decode as float64, so asserting to int panics
- A map value stored as one type is read assuming another
Root Cause
A single-value type assertion x.(T) panics when the dynamic type of x is not exactly T. JSON and interface-heavy code frequently store unexpected concrete types, so the unsafe form is a latent runtime crash.
The Fix
Use the comma-ok assertion to check before use, or a type switch to handle each concrete type (remembering JSON numbers are float64), and validate at the boundary where untyped data enters.
Watch out: Converting JSON numbers straight to int truncates; decode into a typed struct with json.Decoder.UseNumber when precision matters.
func asInt(v any) (int, bool) {
switch n := v.(type) {
case int:
return n, true
case float64: // JSON numbers decode here
return int(n), true
case json.Number:
i, err := n.Int64()
return int(i), err == nil
default:
return 0, false
}
}
if n, ok := asInt(raw); ok {
fmt.Println(n)
}
Verify and Prevent
Rerun with the mismatched input and confirm the guard branch is taken instead of a panic. Add table tests for string, int, and float64 inputs.
Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.
This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.