{"record":{"id":"6dcc4279c161b148","repo":"anomalyco/sst","slug":"json-value-can-t-be-decoded-t-v","errorCode":null,"errorMessage":"JSON value can't be decoded: %T: %v","messagePattern":"JSON value can't be decoded: %T: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/util/kv.go","lineNumber":43,"sourceCode":"\t}\n\tif delim, ok := t.(json.Delim); !ok || delim != '{' {\n\t\treturn fmt.Errorf(\"expect JSON object open with '{'\")\n\t}\n\n\tfor dec.More() {\n\t\tt, err = dec.Token()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tkey, ok := t.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expecting JSON key should be always a string: %T: %v\", t, t)\n\t\t}\n\t\tvar value T\n\t\terr = dec.Decode(&value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"JSON value can't be decoded: %T: %v\", value, value)\n\t\t}\n\t\t*p = append(*p, KeyValuePair[T]{Key: key, Value: value})\n\t}\n\n\t// must end with a delim token '}'\n\tt, err = dec.Token()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif delim, ok := t.(json.Delim); !ok || delim != '}' {\n\t\treturn fmt.Errorf(\"expect JSON object close with '}'\")\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tt, err = dec.Token()\n\tif err != io.EOF {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/internal/util/kv.go#L25-L61","documentation":"After reading each key, UnmarshalJSON decodes the value into T; if the value does not match T (or is malformed) it wraps the failure as 'JSON value can't be decoded: <type>: <value>'. It tells you which key's value failed to fit the declared generic type.","triggerScenarios":"Decoding an object whose values are, say, strings into KeyValuePairs[int] (e.g. {\"port\": \"8080\"} into int), or a value being null/nested where T is a scalar.","commonSituations":"Environment/config files where values are quoted strings but the Go type expects numbers; schema drift where an API started returning objects instead of scalars; a null sneaking into a field typed as non-pointer T.","solutions":["Change T to match the actual value type (e.g. KeyValuePairs[string] or KeyValuePairs[any])","Fix the producer to emit values of type T","Use json.Number/any as T and convert manually after decode"],"exampleFix":"// before\nvar kv KeyValuePairs[int]\njson.Unmarshal([]byte(`{\"port\":\"8080\"}`), &kv)\n// after\nvar kv KeyValuePairs[string]\njson.Unmarshal([]byte(`{\"port\":\"8080\"}`), &kv)\nport, _ := strconv.Atoi(kv[0].Value)","handlingStrategy":"type-guard","validationCode":"var probe map[string]any\nif err := json.Unmarshal(raw, &probe); err != nil {\n    return err\n}\nfor k, v := range probe {\n    if _, ok := v.(string); !ok {\n        return fmt.Errorf(\"key %q value is %T, want string\", k, v)\n    }\n}","typeGuard":"func allValuesOfType[T any](raw []byte) bool {\n    var probe map[string]T\n    return json.Unmarshal(raw, &probe) == nil\n}","tryCatchPattern":"var kv KeyValuePairs[string]\nif err := json.Unmarshal(raw, &kv); err != nil {\n    if strings.Contains(err.Error(), \"can't be decoded\") {\n        return fmt.Errorf(\"value type mismatch; use a looser T like any or json.Number\")\n    }\n    return err\n}","preventionTips":["Match T to the real payload value type; prefer any/json.Number for heterogeneous data","Document the expected value type in the API contract","Add unit tests round-tripping representative payloads"],"tags":["go","json","type-mismatch","unmarshal"],"backgroundTag":"json-type-mismatch","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}