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

  1. Keep every field of the request struct JSON-serializable (strings, numbers, bools, slices, maps of primitives)
  2. Sanitize floats with math.IsNaN/math.IsInf before assigning them to request fields
  3. 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

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.