sipeed/picoclaw · error
marshal request: %w
Error message
marshal request: %w
What it means
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.
Source
Thrown at cmd/membench/llm_client.go:116
MaxTokens: 512,
}
if c.NoThinking {
// llama.cpp: chat_template_kwargs
body.ChatTemplateKwargs = map[string]any{
"enable_thinking": false,
}
// Ollama (0.9+): think field
thinkFalse := false
body.Think = &thinkFalse
// GLM (智谱): thinking field
body.Thinking = map[string]any{
"type": "disabled",
}
}
jsonBody, err := json.Marshal(body)
if err != nil {
return "", fmt.Errorf("marshal request: %w", err)
}
endpoint := strings.TrimRight(c.BaseURL, "/") + "/chat/completions"
req, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewReader(jsonBody))
if err != nil {
return "", fmt.Errorf("create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
if c.APIKey != "" {
req.Header.Set("Authorization", "Bearer "+c.APIKey)
}
var respBody []byte
var lastErr error
for attempt := 0; attempt <= c.MaxRetries; attempt++ {
if attempt > 0 {
backoff := time.Duration(1<<(attempt-1)) * time.Second // 1s, 2s, 4s, ...
log.Printf("LLM retry %d/%d after %v: %v", attempt, c.MaxRetries, backoff, lastErr)View on GitHub (pinned to 49183d7e8d)
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
Example fix
// before
type chatRequestBody struct {
Trace chan string `json:"trace"` // marshal error at runtime
}
// after
type chatRequestBody struct {
Trace []string `json:"trace,omitempty"`
} Defensive patterns
Strategy: validation
Validate before calling
// unit test: marshal a fully populated request before shipping
func TestRequestMarshalable(t *testing.T) {
body := newFullChatRequestBody() // every field populated
if _, err := json.Marshal(body); err != nil {
t.Fatalf("request not JSON-safe: %v", err)
}
} Try / catch
jsonBody, err := json.Marshal(body)
if err != nil {
return "", fmt.Errorf("marshal request (model=%s, msgs=%d): %w", body.Model, len(body.Messages), err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Contributors adding metadata fields to the request struct without keeping them JSON-safe; templating user content that smuggles in a math.NaN score.
Related errors
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/9b13e20955724300.
Report an issue: GitHub.