{"record":{"id":"b0e84a69f3191b5d","repo":"chenhg5/cc-connect","slug":"empty-access-token-in-response","errorCode":null,"errorMessage":"empty access_token in response","messagePattern":"empty access_token in response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":593,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"token request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\traw, _ := io.ReadAll(resp.Body)\n\t\treturn fmt.Errorf(\"token request returned %d: %s\", resp.StatusCode, raw)\n\t}\n\n\tvar result struct {\n\t\tAccessToken string `json:\"access_token\"`\n\t\tExpiresIn   string `json:\"expires_in\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn fmt.Errorf(\"token response decode: %w\", err)\n\t}\n\tif result.AccessToken == \"\" {\n\t\treturn fmt.Errorf(\"empty access_token in response\")\n\t}\n\n\tvar expiresSec int\n\t_, _ = fmt.Sscanf(result.ExpiresIn, \"%d\", &expiresSec)\n\tif expiresSec <= 0 {\n\t\texpiresSec = 7200\n\t}\n\n\tp.tokenMu.Lock()\n\tp.token = result.AccessToken\n\tp.tokenExpiry = time.Now().Add(time.Duration(expiresSec) * time.Second)\n\tp.tokenMu.Unlock()\n\n\tslog.Debug(\"qqbot: access token refreshed\", \"expires_in\", expiresSec)\n\treturn nil\n}\n\n// getAccessToken returns the current token, refreshing if expired or near-expiry.","sourceCodeStart":575,"sourceCodeEnd":611,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L575-L611","documentation":"The token response parsed as valid JSON but the access_token field was empty or missing. refreshToken rejects this explicitly (platform/qqbot/qqbot.go:593) because an empty token cannot authenticate subsequent API calls. This typically means QQ accepted the request but refused to issue a token, often returning an error payload with different field names.","triggerScenarios":"refreshToken decodes a 200 JSON body whose access_token field is \"\" — e.g. QQ returned an error object (code/message) with HTTP 200, or the appId is valid but the app lacks permission for this endpoint.","commonSituations":"App credentials valid but bot not yet published/scoped for the group/c2c APIs; QQ returning an application-level error inside a 200 response; response schema drift where the token lives under a different JSON key.","solutions":["Log the full response body to see the actual fields QQ returned (e.g. an error code/message object).","Verify in the QQ open platform that the bot app has the required scopes and is approved for group/c2c messaging.","Re-check appId/clientSecret — some QQ errors surface as 200 + error body rather than 4xx.","If the key name changed, update cc-connect to a version matching the current QQ Bot API, or patch the result struct's json tag."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// preflight credential check via curl before deploying:\n// curl -s -X POST <tokenURL> -d '{\"appId\":\"...\",\"clientSecret\":\"...\"}' | jq -e '.access_token != \"\"'","typeGuard":null,"tryCatchPattern":"if err := platform.Start(ctx); err != nil {\n    if strings.Contains(err.Error(), \"empty access_token\") {\n        slog.Error(\"QQ refused token issuance (check app scopes/publish status); raw response logged at debug\")\n    }\n}","preventionTips":["Confirm the bot app is approved and has group/c2c message scopes in the QQ open platform console.","Capture full token responses at debug level to see application-level error fields QQ returns inside 200 responses.","Validate credentials with a one-off curl/jq check during deployment.","Keep the SDK/cc-connect version in sync with the current QQ Bot API response format."],"tags":["qqbot","oauth","token","empty-response","unexpected-api-response"],"backgroundTag":"empty-api-response","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"}