chenhg5/cc-connect · error
qq: API %s timeout
Error message
qq: API %s timeout
What it means
Returned by callAPI when no response with the matching echo arrives within the hard-coded 15-second window (select on time.After). The request was written to the WebSocket, but the OneBot server never answered in time — the action is abandoned with this error.
Source
Thrown at platform/qq/qq.go:582
select {
case raw := <-ch:
var resp struct {
Status string `json:"status"`
RetCode int `json:"retcode"`
Data json.RawMessage `json:"data"`
}
if json.Unmarshal(raw, &resp) != nil {
return nil, fmt.Errorf("qq: invalid API response")
}
if resp.RetCode != 0 {
return nil, fmt.Errorf("qq: API %s failed (retcode=%d)", action, resp.RetCode)
}
var result map[string]any
_ = json.Unmarshal(resp.Data, &result)
return result, nil
case <-time.After(15 * time.Second):
return nil, fmt.Errorf("qq: API %s timeout", action)
}
}
// callHTTPAPI calls a OneBot v11 HTTP endpoint (e.g. /upload_group_file).
// Used for file operations — avoids WebSocket message size limits and
// file-path issues across Windows/WSL/Docker boundaries.
// Requires http_url to be configured.
func (p *Platform) callHTTPAPI(action string, params map[string]any) (map[string]any, error) {
if p.httpURL == "" {
return nil, fmt.Errorf("qq: http_url not configured")
}
body, err := json.Marshal(params)
if err != nil {
return nil, err
}
url := p.httpURL + "/" + action
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
if err != nil {View on GitHub (pinned to 4000b2338a)
Solutions
- Retry the call once — transient OneBot slowness (risk control, rate limit) often resolves.
- For large images, switch to the HTTP API path (callHTTPAPI) to avoid WS size/latency limits.
- Verify the OneBot implementation preserves the echo field in responses; if it rewrites it, correlation breaks and every call times out.
- Check OneBot server health/CPU and QQ rate-limit status; scale down message frequency.
- Increase the 15s timeout (code change) if your payloads legitimately take longer.
Defensive patterns
Strategy: retry
Try / catch
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
result, err := p.callAPI(action, params)
if err == nil {
return result, nil
}
lastErr = err
if strings.Contains(err.Error(), "timeout") {
time.Sleep(time.Duration(attempt+1) * time.Second)
continue
}
break
}
return lastErr Prevention
- Route large payloads (images, files) through the HTTP API instead of WS
- Confirm the OneBot implementation echoes the echo field unmodified
- Monitor OneBot server load and QQ rate limits
- Use idempotent actions or dedup when retrying after timeout
- Consider raising the timeout for slow deployments
When it happens
Trigger: Any callAPI (Send, SendImage, resolveGroupName, Start-time calls) where the OneBot side is slow, hung, busy processing a large payload, or the echo correlation never matches because the server drops/reformats the echo field.
Common situations: Large base64 image uploads over WS exceeding the 15s budget; OneBot instance overloaded or rate-limited by QQ; server disconnected right after the write; a OneBot implementation that ignores or rewrites the echo field so the reply never lands in the pending channel.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- qq: send image: %w
- wecom-ws: ack timeout
- read register_ack: %w
- cloud_web: preview_ack timeout
- qq: ws connect failed (%s): %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/676d9ba154af46b1.
Report an issue: GitHub.