chenhg5/cc-connect · error
poll decode: %w
Error message
poll decode: %w
What it means
The MAX Bot API long-poll response body could not be decoded as the expected maxUpdatesResponse JSON. The platform treats any JSON decode failure during polling as a hard error wrapping the underlying cause (%w), because the poll loop cannot progress without a valid updates payload.
Source
Thrown at platform/max/max.go:928
if marker != nil {
q.Set("marker", strconv.FormatInt(*marker, 10))
}
req.URL.RawQuery = q.Encode()
resp, err := p.client.Do(req)
if err != nil {
return nil, fmt.Errorf("poll request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
return nil, fmt.Errorf("poll: HTTP %d: %s", resp.StatusCode, body)
}
var result maxUpdatesResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("poll decode: %w", err)
}
for i := range result.Updates {
p.handleUpdate(ctx, &result.Updates[i])
}
return result.Marker, nil
}
func (p *Platform) handleUpdate(ctx context.Context, upd *maxUpdate) {
switch upd.UpdateType {
case "message_created":
if upd.Message != nil {
p.handleMessage(ctx, upd.Message)
}
case "message_callback":
if upd.Callback != nil {
p.handleCallback(ctx, upd.Callback)View on GitHub (pinned to 4000b2338a)
Solutions
- Log the raw body (read resp.Body before decoding) to see what was actually returned
- Verify apiBase in config.toml points at the correct MAX Bot API URL
- Check for proxies/captive portals returning HTML with 200 status
- Confirm the MAX API version; check result.Updates still unmarshals with the current struct tags
- Add retry/backoff around poll so transient truncation does not kill the session
Example fix
// before
var result maxUpdatesResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("poll decode: %w", err)
}
// after
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
var result maxUpdatesResponse
if err := json.Unmarshal(raw, &result); err != nil {
return nil, fmt.Errorf("poll decode: %w (body: %.256s)", err, raw)
} Defensive patterns
Strategy: retry
Validate before calling
if resp.StatusCode == 200 && !json.Valid(rawBody) { /* skip and re-poll */ } Try / catch
result, err := pollOnce(ctx)
if err != nil {
var jerr *json.SyntaxError
if errors.As(err, &jerr) { logRawBody(); continue } // transient bad body
return err
} Prevention
- Pin apiBase to the documented MAX endpoint
- Alert on repeated poll decode failures (proxy/HTML injection)
- Keep the updates struct tags in sync with the MAX API changelog
When it happens
Trigger: p.client.Do succeeded with HTTP 200 but the body is not valid JSON: truncated response, HTML error page behind a proxy, wrong apiBase pointing at a non-MAX server, or MAX changing the response schema so fields fail to unmarshal.
Common situations: Corporate proxy or captive portal returning HTML for 200 responses; misconfigured apiBase in config.toml (e.g. pointed at a local mock); MAX API schema change on a new undocumented field type; network interruption mid-body.
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
- send permission request: %w
- read permission response: %w
- decode response: %w
- poll failed: %w
- get_qrcode_status json: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/39dc6681b31d079f.
Report an issue: GitHub.