{"record":{"id":"21353e73912a2b5a","repo":"anomalyco/sst","slug":"expecting-json-key-should-be-always-a-string-t","errorCode":null,"errorMessage":"expecting JSON key should be always a string: %T: %v","messagePattern":"expecting JSON key should be always a string: %T: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/util/kv.go","lineNumber":38,"sourceCode":"\n\t// must open 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 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 {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/internal/util/kv.go#L20-L56","documentation":"While iterating the object's key tokens, UnmarshalJSON requires every key to be a JSON string. JSON technically allows only string keys, but raw/duplicate decoding or malformed input can surface a non-string token, producing this typed error with the offending Go type and value.","triggerScenarios":"Input containing a non-string key token — practically seen when decoding malformed/NDJSON-like input into KeyValuePairs, e.g. '{1:\"x\"}' produced by a hand-built JSON string.","commonSituations":"Hand-rolled JSON generation in another language (JS object with numeric keys serialized oddly, or template-built JSON); a middleware or proxy mangling payloads; unquoted keys from lenient serializers (JSON5/HJSON).","solutions":["Fix the producer to emit RFC-8259 JSON with quoted string keys","Run the payload through a strict JSON validator to find the malformed key","If keys are numeric upstream, stringify them before serialization"],"exampleFix":"// before\n{\"123\": \"v\"}  // generated as {123: \"v\"} by a JS template\n// after\nJSON.stringify(obj)  // yields {\"123\":\"v\"} with quoted keys","handlingStrategy":"validation","validationCode":"var probe map[string]json.RawMessage\nif err := json.Unmarshal(raw, &probe); err != nil {\n    return fmt.Errorf(\"not an object with string keys: %w\", err)\n}","typeGuard":"func hasStringKeys(raw []byte) bool {\n    var m map[string]json.RawMessage\n    return json.Unmarshal(raw, &m) == nil\n}","tryCatchPattern":"var kv KeyValuePairs[string]\nif err := json.Unmarshal(raw, &kv); err != nil {\n    if strings.Contains(err.Error(), \"key should be always a string\") {\n        return fmt.Errorf(\"malformed JSON key in payload: %w\", err)\n    }\n    return err\n}","preventionTips":["Never hand-build JSON with string templates; use a serializer","Reject non-standard JSON (JSON5/HJSON) at the boundary","Fuzz/validate inputs in tests with malformed key cases"],"tags":["go","json","unmarshal","malformed-input"],"backgroundTag":"invalid-json-key","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}