{"record":{"id":"ec8fb5089829fa5e","repo":"chenhg5/cc-connect","slug":"qqbot-api-request-failed-w","errorCode":null,"errorMessage":"qqbot: api request failed: %w","messagePattern":"qqbot: api request failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":328,"sourceCode":"\t\t}\n\t\tbodyReader = bytes.NewReader(data)\n\t}\n\n\ttoken, err := p.getAccessToken()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"qqbot: get token: %w\", err)\n\t}\n\n\treq, err := http.NewRequest(method, url, bodyReader)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Set(\"Authorization\", \"QQBot \"+token)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := core.HTTPClient.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"qqbot: api request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// Retry once on 401\n\tif resp.StatusCode == http.StatusUnauthorized {\n\t\tif err := p.refreshToken(); err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: token refresh on 401: %w\", err)\n\t\t}\n\t\ttoken, _ = p.getAccessToken()\n\n\t\tif body != nil {\n\t\t\tdata, _ := json.Marshal(body)\n\t\t\tbodyReader = bytes.NewReader(data)\n\t\t}\n\t\treq2, err := http.NewRequest(method, url, bodyReader)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: build retry request: %w\", err)\n\t\t}","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L310-L346","documentation":"apiRequestJSON wraps any transport-level failure from core.HTTPClient.Do with this error. It means the HTTP round trip to the QQ Bot API never completed: DNS failure, connection refused/reset, TLS error, or context timeout. Unlike 4xx/5xx errors, no HTTP response exists, so the raw underlying error is preserved via %w.","triggerScenarios":"core.HTTPClient.Do returns an error during sendMessage, uploadRichMedia (file upload payload), or ackInteraction — e.g. DNS resolution failure, TCP connect timeout, TLS handshake failure, or request context cancellation while calling the QQ API.","commonSituations":"Host has no internet access or a restrictive firewall/proxy; QQ API temporarily down; oversized upload body hitting an idle-timeout; corporate proxy requiring configuration not present in the environment.","solutions":["Read the wrapped error (%w) to distinguish DNS vs connection vs timeout causes.","Verify outbound network access to the QQ API host (curl the URL from the same machine).","If behind a proxy, set HTTPS_PROXY appropriately for the process.","Retry the operation — transient network blips are common; the code does not retry transport failures.","Check if the request is being cancelled early (context deadline in the caller)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"url := \"https://api.sgroup.qq.com\"\nif err := net.DialTimeout(\"tcp\", url+\":443\", 5*time.Second); err != nil {\n    return fmt.Errorf(\"qqbot API unreachable from this host: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"var netErr net.Error\nif errors.As(err, &netErr) && netErr.Timeout() {\n    // transient: retry with backoff\n    time.Sleep(backoff)\n    return p.sendMessage(ctx, replyCtx, text)\n}","preventionTips":["Ensure stable outbound HTTPS access to the QQ API from the host.","Set proxy env vars explicitly if running behind a corporate proxy.","Add application-level retry with backoff around send operations.","Monitor network health; transport failures are always environmental."],"tags":["qqbot","network","http"],"backgroundTag":"network-request-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}