{"record":{"id":"20efa85f28ed27f1","repo":"chenhg5/cc-connect","slug":"qqbot-build-retry-request-w","errorCode":null,"errorMessage":"qqbot: build retry request: %w","messagePattern":"qqbot: build retry request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":345,"sourceCode":"\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}\n\t\treq2.Header.Set(\"Authorization\", \"QQBot \"+token)\n\t\treq2.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tresp2, err := core.HTTPClient.Do(req2)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: api retry failed: %w\", err)\n\t\t}\n\t\tdefer resp2.Body.Close()\n\n\t\tif resp2.StatusCode >= 300 {\n\t\t\traw, _ := io.ReadAll(resp2.Body)\n\t\t\treturn fmt.Errorf(\"qqbot: api %s %s returned %d (after retry): %s\", method, url, resp2.StatusCode, raw)\n\t\t}\n\t\tif result != nil {\n\t\t\tif err := json.NewDecoder(resp2.Body).Decode(result); err != nil {\n\t\t\t\treturn fmt.Errorf(\"qqbot: decode response: %w\", err)\n\t\t\t}","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L327-L363","documentation":"After a 401 and successful token refresh, apiRequestJSON rebuilds the request with http.NewRequest before retrying. If constructing that retry request fails, this error is raised. This is rare — http.NewRequest only fails on a malformed URL or an invalid method — and points at an internal URL-construction problem rather than the API itself.","triggerScenarios":"HTTP 401 received, token refresh succeeded, but http.NewRequest(method, url, ...) fails while building the retry request — a malformed endpoint URL or invalid HTTP method passed into apiRequestJSON.","commonSituations":"Practically only seen if p.apiBase() or a message/groupOpenID interpolated into the URL is empty or contains characters that break URL parsing; effectively an internal invariant issue.","solutions":["Inspect the wrapped error — it names the URL parse failure.","Verify the groupOpenID/userOpenID in the reply context is a valid non-empty ID.","Check p.apiBase()/config for a malformed api_base value (missing scheme, stray spaces).","If IDs come from an inbound event, re-check that the event was parsed correctly before replying."],"exampleFix":"// before (config.toml)\napi_base = \"qqbot-api.example\"\n// after\napi_base = \"https://api.sgroup.qq.com\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(apiBase)\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid qqbot api_base: %q\", apiBase)\n}","typeGuard":null,"tryCatchPattern":"if err := send(); err != nil {\n    if strings.Contains(err.Error(), \"build retry request\") {\n        slog.Error(\"qqbot URL construction bug\", \"err\", err)\n    }\n}","preventionTips":["Validate api_base in config parses as an absolute URL at startup.","Never pass empty/None openIDs into URL construction; validate IDs before sending.","Treat this error as an internal invariant violation — file a bug rather than retrying."],"tags":["qqbot","http","url"],"backgroundTag":"invalid-url-format","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}