chenhg5/cc-connect · error
tuitui: decode %s response: %w (body_len=%d)
Error message
tuitui: decode %s response: %w (body_len=%d)
What it means
When the caller passes a non-nil out, postJSON unmarshals the whole body directly into it. A body that is not valid JSON for the target type produces this wrapped error, including the endpoint and body length to aid debugging. It fires after the status-code check, so the status was 2xx but the payload didn't decode.
Source
Thrown at platform/tuitui/tuitui.go:737
}
defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(io.LimitReader(resp.Body, maxJSONResponseBytes+1))
if err != nil {
return err
}
if len(body) > maxJSONResponseBytes {
return fmt.Errorf("tuitui: %s response exceeds %d bytes", apiPath, maxJSONResponseBytes)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, body)
}
var apiResp struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if out != nil {
if err := json.Unmarshal(body, out); err != nil {
return fmt.Errorf("tuitui: decode %s response: %w (body_len=%d)", apiPath, err, len(body))
}
return nil
}
if len(bytes.TrimSpace(body)) == 0 {
return nil
}
if err := json.Unmarshal(body, &apiResp); err != nil {
return fmt.Errorf("tuitui: decode %s response: %w (body_len=%d)", apiPath, err, len(body))
}
if apiResp.ErrCode != 0 {
return fmt.Errorf("errcode=%d errmsg=%s", apiResp.ErrCode, apiResp.ErrMsg)
}
return nil
}
func (p *Platform) uploadMedia(ctx context.Context, data []byte, mimeType, filename, mediaType string) (mediaID, outName string, err error) {
var body bytes.Buffer
mw := multipart.NewWriter(&body)View on GitHub (pinned to 4000b2338a)
Solutions
- Print/inspect the body (body_len hints at HTML page vs truncated JSON) at the failing endpoint.
- If the body is HTML from a proxy/WAF, fix the network path or whitelist the API host.
- Compare the actual JSON shape against the caller's out struct and update the struct fields.
- Retry the request — a transient truncation may succeed on retry.
Example fix
// before
var resp struct{ Items []Item `json:"data"` } // API actually returns {"result": [...]}
// after
var resp struct{ Result []Item `json:"result"` } Defensive patterns
Strategy: try-catch
Validate before calling
resp, _ := http.Get(url)
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
return fmt.Errorf("expected JSON, got %s", ct)
} Try / catch
if err := p.postJSON(ctx, path, req, &out); err != nil {
if strings.Contains(err.Error(), "decode") {
slog.Warn("non-JSON 2xx body", "err", err) // inspect body_len
}
return err
} Prevention
- Check Content-Type before JSON decoding when calling APIs manually
- Watch for proxies/WAFs rewriting 200 responses to HTML
- Keep out structs in sync with the Tuitui API schema
When it happens
Trigger: Endpoint returns 200 with an unexpected body for a typed decode: HTML error page from a proxy, truncated JSON, or a response shape that doesn't match the caller's out struct.
Common situations: Captive portal or WAF inserting HTML into 200 responses; Tuitui API response schema change; response truncated by an intermediary.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- decode usage response: %w
- parse JSON from %s: %w
- decode: %w
- parse JSON from %s: %w
- qqbot: decode response: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/3c8f64eef1d68e1d.
Report an issue: GitHub.