{"record":{"id":"e66330a5d93ef643","repo":"chenhg5/cc-connect","slug":"qqbot-marshal-body-w","errorCode":null,"errorMessage":"qqbot: marshal body: %w","messagePattern":"qqbot: marshal body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platform/qqbot/qqbot.go","lineNumber":309,"sourceCode":"\tvar result struct {\n\t\tFileInfo string `json:\"file_info\"`\n\t}\n\tif err := p.apiRequestJSON(\"POST\", url, reqBody, &result); err != nil {\n\t\treturn \"\", err\n\t}\n\tif result.FileInfo == \"\" {\n\t\treturn \"\", fmt.Errorf(\"qqbot: upload rich media: empty file_info\")\n\t}\n\treturn result.FileInfo, nil\n}\n\n// apiRequestJSON is like apiRequest but also decodes the response body into result.\nfunc (p *Platform) apiRequestJSON(method, url string, body any, result any) error {\n\tvar bodyReader io.Reader\n\tif body != nil {\n\t\tdata, err := json.Marshal(body)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: marshal body: %w\", err)\n\t\t}\n\t\tbodyReader = bytes.NewReader(data)\n\t}\n\n\ttoken, err := p.getAccessToken()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"qqbot: get token: %w\", err)\n\t}\n\n\treq, err := http.NewRequest(method, url, bodyReader)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Set(\"Authorization\", \"QQBot \"+token)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := core.HTTPClient.Do(req)\n\tif err != nil {","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L291-L327","documentation":"apiRequestJSON serializes the request body to JSON before issuing the HTTP call; a json.Marshal failure is wrapped with this message. In practice this is rare because bodies are maps/slices of JSON-safe values, but non-serializable types (channels, funcs, NaN floats, cycles) trigger it before any network I/O happens.","triggerScenarios":"Passing a body map[string]any containing a value that cannot be marshaled — e.g. a func, channel, sync.Mutex stored in an option, cyclic structure, or math.NaN()/Inf() float.","commonSituations":"Callers injecting extra fields into request bodies with non-JSON-safe values; debugging code that stuffs a request context object into the body; copy-pasted code passing structs with unexported-only or unsupported fields.","solutions":["Inspect the wrapped inner error (%w): json.UnsupportedTypeError names the offending type, UnsupportedValueError names the value.","Remove or convert non-serializable values (func/channel/mutex) from the request body.","Replace NaN/Inf floats with strings or omit them.","If a custom body is being injected, pre-validate it with json.Marshal in a test.","Keep bodies restricted to the adapter's internally built map[string]any payloads."],"exampleFix":"// before\nbody := map[string]any{\"file_type\": 4, \"callback\": func() {}} // func not JSON-encodable\nerr := p.apiRequestJSON(\"POST\", url, body, &result) // marshal body error\n// after\nbody := map[string]any{\"file_type\": 4, \"file_data\": b64, \"srv_send_msg\": false}\nerr := p.apiRequestJSON(\"POST\", url, body, &result)","handlingStrategy":"try-catch","validationCode":"if _, err := json.Marshal(body); err != nil {\n    return fmt.Errorf(\"request body not JSON-encodable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := p.apiRequestJSON(\"POST\", url, body, &result); err != nil {\n    var uErr *json.UnsupportedTypeError\n    var vErr *json.UnsupportedValueError\n    if errors.As(err, &uErr) || errors.As(err, &vErr) {\n        // bad value in body map; drop/convert it, no retry will help\n    }\n}","preventionTips":["Keep request bodies to JSON-safe primitives, slices, and maps only.","Never insert funcs, channels, or mutexes into option/body maps.","Sanitize float values (NaN/Inf) before adding them to a body.","Add a unit test that marshals every body the adapter builds."],"tags":["qqbot","json","serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}