chenhg5/cc-connect · warning

qqbot: marshal body: %w

Error message

qqbot: marshal body: %w

What it means

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.

Source

Thrown at platform/qqbot/qqbot.go:309

	var result struct {
		FileInfo string `json:"file_info"`
	}
	if err := p.apiRequestJSON("POST", url, reqBody, &result); err != nil {
		return "", err
	}
	if result.FileInfo == "" {
		return "", fmt.Errorf("qqbot: upload rich media: empty file_info")
	}
	return result.FileInfo, nil
}

// apiRequestJSON is like apiRequest but also decodes the response body into result.
func (p *Platform) apiRequestJSON(method, url string, body any, result any) error {
	var bodyReader io.Reader
	if body != nil {
		data, err := json.Marshal(body)
		if err != nil {
			return fmt.Errorf("qqbot: marshal body: %w", err)
		}
		bodyReader = bytes.NewReader(data)
	}

	token, err := p.getAccessToken()
	if err != nil {
		return fmt.Errorf("qqbot: get token: %w", err)
	}

	req, err := http.NewRequest(method, url, bodyReader)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "QQBot "+token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := core.HTTPClient.Do(req)
	if err != nil {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped inner error (%w): json.UnsupportedTypeError names the offending type, UnsupportedValueError names the value.
  2. Remove or convert non-serializable values (func/channel/mutex) from the request body.
  3. Replace NaN/Inf floats with strings or omit them.
  4. If a custom body is being injected, pre-validate it with json.Marshal in a test.
  5. Keep bodies restricted to the adapter's internally built map[string]any payloads.

Example fix

// before
body := map[string]any{"file_type": 4, "callback": func() {}} // func not JSON-encodable
err := p.apiRequestJSON("POST", url, body, &result) // marshal body error
// after
body := map[string]any{"file_type": 4, "file_data": b64, "srv_send_msg": false}
err := p.apiRequestJSON("POST", url, body, &result)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(body); err != nil {
    return fmt.Errorf("request body not JSON-encodable: %w", err)
}

Try / catch

if err := p.apiRequestJSON("POST", url, body, &result); err != nil {
    var uErr *json.UnsupportedTypeError
    var vErr *json.UnsupportedValueError
    if errors.As(err, &uErr) || errors.As(err, &vErr) {
        // bad value in body map; drop/convert it, no retry will help
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/e66330a5d93ef643. Report an issue: GitHub.