chenhg5/cc-connect · error
token response decode: %w
Error message
token response decode: %w
What it means
The token endpoint returned HTTP 200 but its body could not be parsed as the expected JSON ({"access_token":..., "expires_in":...}). refreshToken wraps the json.Decoder error with this message (platform/qqbot/qqbot.go:590). Usually indicates an HTML error page, proxy interstitial, or an API response-format change.
Source
Thrown at platform/qqbot/qqbot.go:590
})
resp, err := core.HTTPClient.Post(tokenURL, "application/json", bytes.NewReader(body))
if err != nil {
return fmt.Errorf("token request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
raw, _ := io.ReadAll(resp.Body)
return fmt.Errorf("token request returned %d: %s", resp.StatusCode, raw)
}
var result struct {
AccessToken string `json:"access_token"`
ExpiresIn string `json:"expires_in"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return fmt.Errorf("token response decode: %w", err)
}
if result.AccessToken == "" {
return fmt.Errorf("empty access_token in response")
}
var expiresSec int
_, _ = fmt.Sscanf(result.ExpiresIn, "%d", &expiresSec)
if expiresSec <= 0 {
expiresSec = 7200
}
p.tokenMu.Lock()
p.token = result.AccessToken
p.tokenExpiry = time.Now().Add(time.Duration(expiresSec) * time.Second)
p.tokenMu.Unlock()
slog.Debug("qqbot: access token refreshed", "expires_in", expiresSec)
return nilView on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the wrapped decode error; if it mentions HTML, a proxy/portal is intercepting traffic — bypass or fix the proxy.
- Verify the token endpoint URL has not been changed and the deployed version of cc-connect is current.
- Test the endpoint directly with curl using the same appId/clientSecret payload.
- Update cc-connect if QQ changed the response schema.
Defensive patterns
Strategy: retry
Validate before calling
// preflight: ensure the token endpoint returns JSON
resp, err := http.Post(tokenURL, "application/json", body)
if err == nil {
var probe map[string]any
if json.NewDecoder(resp.Body).Decode(&probe) != nil {
log.Println("token endpoint not returning JSON — check proxy/portal")
}
resp.Body.Close()
} Try / catch
if err := platform.Start(ctx); err != nil {
if strings.Contains(err.Error(), "token response decode") {
slog.Error("qqbot token endpoint returned non-JSON (proxy/portal or API change)", "err", err)
// retry once, then fall back to manual endpoint check
}
} Prevention
- Run on hosts without captive portals or HTML-injecting transparent proxies.
- Keep cc-connect updated so response schema changes in the QQ API are picked up.
- Monitor for repeated decode errors — they indicate environmental interception, not transient faults.
- Pin an egress path (allowlist the QQ API domain) in corporate networks.
When it happens
Trigger: refreshToken receives a 200 response whose body is not valid JSON or not the expected shape: captive portal/proxy HTML page, truncated response, or QQ changing the token endpoint response format.
Common situations: Hotel/corporate captive portals intercepting HTTPS (rare with TLS), a transparent proxy returning HTML, or running an old cc-connect version against an updated QQ API.
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
- token request failed: %w
- token request returned %d: %s
- empty access_token in response
- parse auth.json: %w
- invalid json response: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/19d0128972a1aef6.
Report an issue: GitHub.