{"record":{"id":"9b13e20955724300","repo":"sipeed/picoclaw","slug":"marshal-request-w","errorCode":null,"errorMessage":"marshal request: %w","messagePattern":"marshal request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/membench/llm_client.go","lineNumber":116,"sourceCode":"\t\tMaxTokens:   512,\n\t}\n\tif c.NoThinking {\n\t\t// llama.cpp: chat_template_kwargs\n\t\tbody.ChatTemplateKwargs = map[string]any{\n\t\t\t\"enable_thinking\": false,\n\t\t}\n\t\t// Ollama (0.9+): think field\n\t\tthinkFalse := false\n\t\tbody.Think = &thinkFalse\n\t\t// GLM (智谱): thinking field\n\t\tbody.Thinking = map[string]any{\n\t\t\t\"type\": \"disabled\",\n\t\t}\n\t}\n\n\tjsonBody, err := json.Marshal(body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"marshal request: %w\", err)\n\t}\n\n\tendpoint := strings.TrimRight(c.BaseURL, \"/\") + \"/chat/completions\"\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", endpoint, bytes.NewReader(jsonBody))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tif c.APIKey != \"\" {\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+c.APIKey)\n\t}\n\n\tvar respBody []byte\n\tvar lastErr error\n\tfor attempt := 0; attempt <= c.MaxRetries; attempt++ {\n\t\tif attempt > 0 {\n\t\t\tbackoff := time.Duration(1<<(attempt-1)) * time.Second // 1s, 2s, 4s, ...\n\t\t\tlog.Printf(\"LLM retry %d/%d after %v: %v\", attempt, c.MaxRetries, backoff, lastErr)","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/membench/llm_client.go#L98-L134","documentation":"json.Marshal failed on the chat-completion request body in membench's LLM client (llm_client.go:116). The body struct contains only strings, ints, and the think/thinking toggles, so marshalling is deterministic and this error is near-unreachable — it would require an unsupported value (NaN float, chan, func) introduced by a future field or a custom Message payload.","triggerScenarios":"Extending chatRequestBody with a chan/func field or a float that can be NaN/Inf; passing an invalid custom type through Messages; a fork adding map[string]any user input containing unmarshalable values.","commonSituations":"Contributors adding metadata fields to the request struct without keeping them JSON-safe; templating user content that smuggles in a math.NaN score.","solutions":["Keep every field of the request struct JSON-serializable (strings, numbers, bools, slices, maps of primitives)","Sanitize floats with math.IsNaN/math.IsInf before assigning them to request fields","Unit-test Marshal on a fully populated request struct to catch this at build time, not runtime"],"exampleFix":"// before\ntype chatRequestBody struct {\n    Trace chan string `json:\"trace\"` // marshal error at runtime\n}\n\n// after\ntype chatRequestBody struct {\n    Trace []string `json:\"trace,omitempty\"`\n}","handlingStrategy":"validation","validationCode":"// unit test: marshal a fully populated request before shipping\nfunc TestRequestMarshalable(t *testing.T) {\n    body := newFullChatRequestBody() // every field populated\n    if _, err := json.Marshal(body); err != nil {\n        t.Fatalf(\"request not JSON-safe: %v\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":"jsonBody, err := json.Marshal(body)\nif err != nil {\n    return \"\", fmt.Errorf(\"marshal request (model=%s, msgs=%d): %w\", body.Model, len(body.Messages), err)\n}","preventionTips":["Keep request structs limited to strings, numbers, bools, slices, and primitive maps","Sanitize user-supplied floats with math.IsNaN/math.IsInf","Add a marshalling unit test whenever a field is added to the request struct"],"tags":["go","json","llm-client","request"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}