chenhg5/cc-connect · error
token request returned %d: %s
Error message
token request returned %d: %s
What it means
The OAuth token endpoint answered, but with a non-200 status. refresh token returns this error including the status code and the raw response body (platform/qqbot/qqbot.go:582). Almost always this means the appId/clientSecret credentials were rejected (401/400) or the service is degraded (5xx).
Source
Thrown at platform/qqbot/qqbot.go:582
// ---------------------------------------------------------------------------
// OAuth2 Token Management
// ---------------------------------------------------------------------------
func (p *Platform) refreshToken() error {
body, _ := json.Marshal(map[string]string{
"appId": p.appID,
"clientSecret": p.appSecret,
})
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
}View on GitHub (pinned to 4000b2338a)
Solutions
- Read the status code and body in the error: 401/400 means fix appId/clientSecret in config.toml.
- Confirm the app is still active on the QQ open platform console and the secret has not been rotated.
- If 5xx, retry later — the QQ Bot service may be degraded.
- Avoid tight refresh loops; the platform already refreshes lazily, so hammering the endpoint can trigger rate limits.
Example fix
// before
"qqbot": { appId = "old-id", appSecret = "stale-secret" }
// after
"qqbot": { appId = "<current-app-id>", appSecret = "<current-secret-from-qq-console>" } Defensive patterns
Strategy: try-catch
Validate before calling
// validate credentials at startup before wiring the platform:
if appID == "" || appSecret == "" { return fmt.Errorf("qqbot appId/appSecret required in config") } Try / catch
if err := platform.Start(ctx); err != nil {
if strings.Contains(err.Error(), "token request returned 401") || strings.Contains(err.Error(), "token request returned 400") {
return fmt.Errorf("qqbot credentials rejected — check appId/appSecret: %w", err)
}
return err
} Prevention
- Store appId/appSecret in config.toml and rotate them in the QQ console before old secrets expire.
- Distinguish 4xx (credentials — fix config) from 5xx (service — retry later) when reading the error.
- Never commit secrets; load them from env/secret store to avoid accidental revocation/leaks.
- Check startup logs once at deploy time; a bad secret fails fast on the first token request.
When it happens
Trigger: refreshToken gets resp.StatusCode != 200: wrong or revoked appSecret (401/400), malformed appId, QQ Bot API outage (500/503), or rate limiting on the token endpoint.
Common situations: Typo'd or rotated-out appSecret in config.toml; QQ open-platform app disabled; clock/host issues causing 4xx; shared IP rate-limited by QQ.
Related errors
- token request failed: %w
- token response decode: %w
- empty access_token in response
- get access token: %w
- %s: %s failed after token refresh attempt: %w (original erro
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/031dc20637764d19.
Report an issue: GitHub.