panic: interface conversion: interface {} is string, not int
panic: interface conversion: interface is nil, not main.MyType
This panic occurs when you perform a type assertion on an interface value, but the underlying type doesnβt match what youβre asserting. Go panics immediately rather than returning a zero value, which crashes your program.
Why this happens
In Go, interfaces hold a value and a type. A type assertion i.(T) extracts the concrete value of type T from the interface i. If i doesnβt actually hold a value of type T, Go panics.
This happens in three scenarios:
- The interface holds a different type than expected
- The interface is
nil(holds no value at all) - JSON unmarshaling stores unexpected types in
interface{}values
Fix 1: Use the comma-ok pattern
The safest way to do type assertions. The second return value tells you whether the assertion succeeded.
// β Direct assertion β panics if wrong type
var i interface{} = "hello"
n := i.(int) // panic: interface conversion: interface {} is string, not int
// β
Comma-ok pattern β never panics
n, ok := i.(int)
if !ok {
fmt.Println("i is not an int, it's:", reflect.TypeOf(i))
return
}
fmt.Println("value:", n)
Rule of thumb: Always use comma-ok unless youβre 100% certain of the type (e.g., right after a type switch case).
This pattern works for any type assertion:
s, ok := i.(string)
m, ok := i.(map[string]interface{})
err, ok := i.(error)
r, ok := i.(io.Reader)
Fix 2: Check for nil before asserting
A nil interface has no underlying value or type. Any type assertion on nil panics.
// β Asserting on nil interface
var i interface{} // nil
s := i.(string) // panic: interface conversion: interface is nil, not string
// β
Check for nil first
if i == nil {
fmt.Println("interface is nil, using default")
return ""
}
s, ok := i.(string)
if !ok {
return ""
}
return s
Common source of nil interfaces: Functions that return interface{} or error β when the function returns nil, the caller might still try to assert a type on it.
func getConfig(key string) interface{} {
val, exists := configMap[key]
if !exists {
return nil // Caller must check for nil before asserting
}
return val
}
// β Panics if key doesn't exist
host := getConfig("host").(string)
// β
Safe
if val := getConfig("host"); val != nil {
host, _ = val.(string)
}
Fix 3: Use a type switch for multiple possible types
When an interface could hold several types, a type switch is cleaner and safer than multiple comma-ok assertions.
func processValue(i interface{}) string {
switch v := i.(type) {
case string:
return v
case int:
return strconv.Itoa(v)
case float64:
return fmt.Sprintf("%.2f", v)
case bool:
return strconv.FormatBool(v)
case nil:
return "<nil>"
case []interface{}:
return fmt.Sprintf("array with %d items", len(v))
default:
return fmt.Sprintf("unknown type: %T", v)
}
}
Type switches never panic β the default case catches anything unexpected. Use them when processing data from external sources (APIs, config files, user input).
Fix 4: JSON unmarshaling stores float64, not int
This is one of the most common Go gotchas. When you unmarshal JSON into interface{} or map[string]interface{}, all numbers become float64 β never int.
var data map[string]interface{}
json.Unmarshal([]byte(`{"count": 42, "name": "test"}`), &data)
// β JSON numbers are float64, not int
count := data["count"].(int) // panic!
// β
Assert to float64, then convert
count := int(data["count"].(float64))
// β
Better: unmarshal into a typed struct
type Response struct {
Count int `json:"count"`
Name string `json:"name"`
}
var resp Response
json.Unmarshal([]byte(`{"count": 42, "name": "test"}`), &resp)
// resp.Count is already int
JSON type mapping to Go interface{}:
| JSON type | Go type |
|---|---|
| number | float64 |
| string | string |
| boolean | bool |
| null | nil |
| array | []interface{} |
| object | map[string]interface{} |
Best practice: Avoid map[string]interface{} when you know the structure. Define a struct with proper types and let json.Unmarshal handle the conversion.
Fix 5: Interface not implemented (compile-time error)
This is a different kind of interface error β it happens at compile time, not runtime. But itβs related and often confused with the panic.
type Writer interface {
Write(data []byte) (int, error)
}
type MyLogger struct{}
// β Compile error: MyLogger does not implement Writer
var w Writer = MyLogger{}
// β
Implement the method with the exact signature
func (m MyLogger) Write(data []byte) (int, error) {
fmt.Print(string(data))
return len(data), nil
}
var w Writer = MyLogger{} // Now works
Tip: Use a compile-time check to verify interface implementation:
// This line fails to compile if MyLogger doesn't implement Writer
var _ Writer = (*MyLogger)(nil)
Fix 6: Pointer vs value receiver mismatch
A subtle variant β your type implements the interface with a pointer receiver, but youβre using a value.
type Stringer interface {
String() string
}
type User struct{ Name string }
// Method on *User (pointer receiver)
func (u *User) String() string {
return u.Name
}
// β Value doesn't implement the interface (pointer does)
var s Stringer = User{Name: "Alice"} // Compile error!
// β
Use a pointer
var s Stringer = &User{Name: "Alice"} // Works
Fix 7: Recovering from interface panics
If you canβt guarantee the type at compile time (e.g., processing plugin output), use recover as a last resort:
func safeAssert(i interface{}) (result string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("type assertion failed: %v", r)
}
}()
result = i.(string)
return result, nil
}
Warning: Using recover to handle type assertions is a code smell. Prefer comma-ok or type switches. Only use recover at boundaries where you process untrusted data.
Debugging tips
-
Print the actual type before asserting:
fmt.Printf("type: %T, value: %v\n", i, i) -
Use
reflect.TypeOffor more detail:fmt.Println(reflect.TypeOf(i)) // Shows the concrete type -
Check if nil β
fmt.Printf("%v", i)prints<nil>for nil interfaces. -
Stack trace β The panic message includes the file and line number. Look at whatβs being assigned to the interface upstream.
FAQ
Whatβs the difference between a nil interface and an interface holding a nil pointer?
Theyβre different! A nil interface has no type or value. An interface holding a nil pointer has a type but a nil value β and itβs NOT equal to nil.
var i interface{} = (*string)(nil)
fmt.Println(i == nil) // false! The interface holds a type (*string)
This is a notorious Go gotcha. See the Go nil pointer dereference fix for more.
Can I assert to an interface type?
Yes. You can assert that a value implements another interface:
var i interface{} = os.Stdout
w, ok := i.(io.Writer) // true β *os.File implements io.Writer
Does the comma-ok pattern have a performance cost?
No measurable cost. The compiler generates the same type check either way β the only difference is whether it panics or returns false.
How do I handle this in generic code (Go 1.18+)?
With generics, you can often avoid interface{} entirely:
func process[T any](val T) T {
// No type assertion needed β T is known at compile time
return val
}