json: cannot unmarshal string into Go value of type int

Quick answer

Go's encoding/json is strictly typed: this error means the JSON value's type doesn't match your struct field — e.g. JSON "42" (string) into an int. Fix the field type to match the JSON, or, if the API sends numbers as quoted strings, add the ,string struct tag (`json:"id,string"`). For shape mismatches, make the destination type match the actual payload.

The exact error string

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    data := []byte(`{"id": "42", "name": "Ada"}`) // id is a STRING in the JSON
    var u User
    err := json.Unmarshal(data, &u)
    fmt.Println(err)
    // json: cannot unmarshal string into Go struct field User.id of type int
}

The message names both sides: the JSON type (string) and the Go type it couldn't fit (int), plus the field path (User.id). That tells you exactly which field to fix.

Which JSON values fit which Go fields

JSON valueGo fieldResult
42int✅ ok
"42"int❌ cannot unmarshal
"42"int with ,string✅ ok
42string❌ cannot unmarshal
truebool✅ ok
"true"bool❌ unless ,string (only "true"/"false", not "1"/"0")
nullint✅ leaves the zero value (0)
null*int✅ becomes nil

Cause 1: the field type is simply wrong

If the JSON genuinely holds a string, number, or bool, make the Go field that type. Match each field to what the payload actually sends:

// JSON: {"id": 42, "active": true, "score": 9.5}
type User struct {
    ID     int     `json:"id"`
    Active bool    `json:"active"`
    Score  float64 `json:"score"`
}

Cause 2: the API sends numbers as strings

Some APIs quote their numbers ("id": "42"). Keep the numeric Go type and add the ,string option to the tag — encoding/json will parse the quoted value:

type User struct {
    ID    int     `json:"id,string"`     // accepts "42"
    Score float64 `json:"score,string"`  // accepts "9.5"
}

Caveat for bool: ,string also works on a bool field, but it only accepts the quoted strings "true" and "false"not "1"/"0" or "yes"/"no". For those, decode the raw value and convert yourself in a custom UnmarshalJSON.

Cause 3: object vs array (shape mismatch)

cannot unmarshal object into Go value of type []User means the JSON is an object but you unmarshalled into a slice (or the reverse). The payload is usually wrapped — match the destination to it:

// JSON: {"data": [{"id":1}, {"id":2}]}  — an object wrapping an array

var users []User
json.Unmarshal(data, &users)        // ❌ cannot unmarshal object into []User

type Resp struct {
    Data []User `json:"data"`
}
var r Resp
json.Unmarshal(data, &r)            // ✅ r.Data is the slice

Cause 4: a field that varies, or big numbers

For a field whose type changes, defer decoding with json.RawMessage or a custom UnmarshalJSON. For numbers of unknown shape (and to avoid the float64 precision trap when decoding into interface{}), use UseNumber():

dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()                      // numbers -> json.Number, not float64

var m map[string]interface{}
dec.Decode(&m)

// use a safe type assertion — m["id"] may be absent or another type
if n, ok := m["id"].(json.Number); ok {
    id, _ := n.Int64()                   // exact, no precision loss
    fmt.Println(id)
}

Nullable fields: handling JSON null

An API that sends {"age": null} is a frequent trigger. A plain value field decodes null as the zero value, so you can't tell "missing/null" from "actually zero." Use a pointer (or a database/sql Null type) when that distinction matters:

// JSON: {"age": null}
type User struct {
    Age *int `json:"age"`        // nil when null; non-nil pointer to 0 when 0
}

// database/sql alternative when reading from a DB:
type Row struct {
    Age sql.NullInt64            // .Valid is false when the value was null
}

What interface{} decodes JSON into

When you decode into interface{} or map[string]interface{}, Go maps each JSON type to a fixed Go type. Knowing this table prevents the wrong type assertion that causes a panic or a follow-on error:

JSONGo type inside interface{}
objectmap[string]interface{}
array[]interface{}
stringstring
numberfloat64 (or json.Number with UseNumber())
true / falsebool
nullnil

Catch unexpected fields with DisallowUnknownFields

By default Go silently ignores JSON keys that have no matching struct field, which hides typos and schema drift. Make an unknown key an error during decoding:

dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
if err := dec.Decode(&v); err != nil {
    // json: unknown field "xyz" — a key your struct doesn't define
}

Debugging checklist

Frequently Asked Questions

What does 'json: cannot unmarshal string into Go value of type int' mean?

It means the JSON contained a string where your Go struct field is an int (or another mismatch). Go's encoding/json is strictly typed, so a JSON value like "42" cannot go into an int field. Either the field type is wrong, or the API sends numbers as quoted strings.

How do I accept a number that the API sends as a string?

Add the ,string option to the struct tag: ID int `json:"id,string"`. This tells encoding/json the value arrives as a quoted string like "42" and should be parsed into the numeric field. It works for int, uint, and float fields; for bool it accepts only the quoted strings "true"/"false", not "1"/"0".

What does 'cannot unmarshal object into Go value of type []T' mean?

The JSON is an object ({...}) but your destination is a slice ([]T), or vice versa. The shapes do not match. Check whether the payload is wrapped — for example {"data": [...]} — and unmarshal into a struct whose field matches, or change the destination type to fit the actual JSON.

How do I handle a field that can be more than one type?

Use json.RawMessage to defer decoding, or implement a custom UnmarshalJSON on a wrapper type that inspects the raw bytes and decodes accordingly. For numbers of unknown int/float shape, decode into interface{} with Decoder.UseNumber() so they become json.Number instead of float64.

Why does my number become a float64?

When you unmarshal JSON into interface{} or map[string]interface{}, every number becomes float64 by default, which loses precision on large integers. Call decoder.UseNumber() so numbers decode as json.Number (a string-backed type) that you convert exactly with .Int64() or .Float64().

How do I see exactly what the JSON contains?

Print the raw bytes before unmarshalling, or paste the payload into a JSON formatter to see each value's real type. The error names the JSON type and the Go type it could not fit, so confirming the actual shape tells you whether to fix the struct or the producer.

Check what types your JSON really uses

Paste the payload into the formatter to see whether each value is a string, number, object, or array — nothing is uploaded to a server. Building the struct from scratch? Paste the JSON into the TypeScript generator and adapt the types — the field types map directly to Go.

JSON Formatter JSON Validator All Error References
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.