{"record":{"id":"c7f12cf8669ac7b6","repo":"chenhg5/cc-connect","slug":"wecom-get-access-token-w","errorCode":null,"errorMessage":"wecom: get access_token: %w","messagePattern":"wecom: get access_token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/wecom.go","lineNumber":464,"sourceCode":"\t\t\t\"msg_type\", msg.MsgType,\n\t\t\t\"msg_id\", msg.MsgId,\n\t\t\t\"from_user\", msg.FromUserName)\n\t}\n}\n\nfunc (p *Platform) Reply(ctx context.Context, rctx any, content string) error {\n\trc, ok := rctx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"wecom: invalid reply context type %T\", rctx)\n\t}\n\tif content == \"\" {\n\t\treturn nil\n\t}\n\n\taccessToken, err := p.getAccessToken()\n\tif err != nil {\n\t\tslog.Error(\"wecom: get access_token failed\", \"error\", err)\n\t\treturn fmt.Errorf(\"wecom: get access_token: %w\", err)\n\t}\n\n\tif !p.enableMarkdown {\n\t\tcontent = core.StripMarkdown(content)\n\t}\n\n\tchunks := splitByBytes(content, 2000)\n\tfor i, chunk := range chunks {\n\t\tvar sendErr error\n\t\tif p.enableMarkdown {\n\t\t\tsendErr = p.sendMarkdown(accessToken, rc.userID, chunk)\n\t\t} else {\n\t\t\tsendErr = p.sendText(accessToken, rc.userID, chunk)\n\t\t}\n\t\tif sendErr != nil {\n\t\t\tslog.Error(\"wecom: send failed\", \"user\", rc.userID, \"chunk\", i, \"error\", sendErr)\n\t\t\treturn sendErr\n\t\t}","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/wecom.go#L446-L482","documentation":"Before replying, Reply() must obtain a WeCom access_token via getAccessToken() (which exchanges corp_id/corp_secret with the WeCom API and caches it). If that call fails — network error, invalid credentials, WeCom error response — the failure is logged and returned wrapped as \"wecom: get access_token: %w\".","triggerScenarios":"getAccessToken returning an error during Reply: unreachable/blocked qyapi.weixin.qq.com, wrong corp_id or corp_secret, expired or revoked secret, WeCom returning errcode != 0 (e.g. 40013 invalid corpid, 40001 invalid credential), or the outbound HTTP request timing out through a broken proxy.","commonSituations":"Corp secret rotated in the admin console but not in config; server without outbound internet access or a misconfigured proxy; IP not in the WeCom app's trusted IP allowlist; firewall blocking egress to the API domain.","solutions":["Verify corp_secret/corp_id are current (re-copy from WeCom admin console after any rotation)","Check the server can reach the API base URL (curl the gettoken endpoint) and that the app's IP allowlist includes your egress IP","Inspect the wrapped cause (%w) in the error/log line to see whether it is a network error or a WeCom errcode","If behind a proxy, confirm the proxy option is correct and the proxy allows the API host","Retry after fixing credentials; access_token failures are often transient on network blips"],"exampleFix":"// before\n\"corp_secret\": \"old-rotated-secret\" // 40001 invalid credential\n// after\n\"corp_secret\": \"newSecretFromWeComAdminConsole\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := p.Reply(ctx, rctx, msg); err != nil {\n\tif strings.Contains(err.Error(), \"get access_token\") {\n\t\t// check corp credentials/network, maybe backoff and retry\n\t}\n\tslog.Warn(\"reply failed\", \"err\", err)\n}","preventionTips":["Monitor the wrapped cause for errcode 40001 (rotate-in new corp_secret promptly)","Keep corp_secret in a secret store and update it on rotation","Verify egress and trusted-IP allowlist during environment provisioning","Log and alert on token failures so outages are caught early"],"tags":["wecom","network","authentication","access-token"],"backgroundTag":"oauth-token-exchange-failed","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"}