Tencent/WeKnora · error
get token error: code=%d msg=%s
Error message
get token error: code=%d msg=%s
What it means
getAccessToken fetches a WeCom access token using corpid + corpsecret and caches it. A non-zero errcode in the token response is wrapped as "get token error: code=%d msg=%s". This typically means the corp credentials used to obtain the token are wrong or the IP is not whitelisted.
Source
Thrown at internal/im/wecom/webhook_adapter.go:420
}
resp, err := httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("request access token: %w", err)
}
defer resp.Body.Close()
var result struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"` // seconds
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", fmt.Errorf("decode token response: %w", err)
}
if result.ErrCode != 0 {
return "", fmt.Errorf("get token error: code=%d msg=%s", result.ErrCode, result.ErrMsg)
}
a.tokenCache = result.AccessToken
// Cache with 5-minute safety margin
ttl := time.Duration(result.ExpiresIn) * time.Second
if ttl > 5*time.Minute {
ttl -= 5 * time.Minute
}
a.tokenExpAt = time.Now().Add(ttl)
return a.tokenCache, nil
}
// verifySignature verifies the WeCom callback signature using constant-time comparison.
func (a *WebhookAdapter) verifySignature(signature, timestamp, nonce, encrypt string) bool {
parts := []string{a.token, timestamp, nonce, encrypt}
sort.Strings(parts)
combined := strings.Join(parts, "")View on GitHub (pinned to 988cbb0330)
Solutions
- Check the errcode against WeCom docs: 40001 → fix corpsecret; 40013 → fix corpid; 60020 → add server IP to trusted IPs.
- Verify the corpid and corpsecret in the channel credentials match the WeCom admin console for the correct app.
- If credentials were rotated, update them and clear the adapter's tokenCache.
- Ensure the outbound egress IP is stable and added to the app's trusted IP configuration.
Example fix
// before
ch.Credentials = `{"corp_id":"old-id","corp_secret":"revoked-secret"}`
// after
ch.Credentials = `{"corp_id":"ww-current-id","corp_secret":"<current-secret>"}` Defensive patterns
Strategy: retry
Validate before calling
if corpID == "" || corpSecret == "" {
return fmt.Errorf("wecom token fetch skipped: corp_id/corp_secret not configured")
} Try / catch
token, err := a.getAccessToken(ctx)
if err != nil {
var code int
if n, _ := fmt.Sscanf(err.Error(), "get token error: code=%d", &code); n == 1 {
switch code {
case 40001:
return nil, fmt.Errorf("wecom corpsecret invalid — update channel credentials")
case 40013:
return nil, fmt.Errorf("wecom corpid invalid — update channel credentials")
case 60020:
return nil, fmt.Errorf("server IP not in wecom trusted IP list")
}
}
return nil, err // transient network error — caller may retry
} Prevention
- Rotate corpid/secret in both WeCom admin and channel credentials together.
- Pin egress IPs and register them in the app's trusted IP list.
- Cache tokens with a safety margin (as the adapter does) to reduce fetch frequency.
- Monitor token-fetch failures separately from send failures to isolate auth issues.
When it happens
Trigger: Called by SendReply and DownloadFile whenever a fresh token is needed and WeCom's gettoken endpoint returns errcode != 0 — e.g. 40001 invalid corpsecret, 40013 invalid corpid, 60020 IP not in the app's trusted IP list.
Common situations: Corp secret rotated in WeCom admin without updating channel credentials; typo in corp_id; server IP missing from the app's trusted-IP allowlist; clock skew invalidating tokens instantly.
Related errors
- appchat api error: code=%d msg=%s
- wecom api error: code=%d msg=%s
- Zhipu API error (%s): %s
- Zhipu API returned status %d (%s): %s
- token not yet valid
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/548e5b4e5af3b2e8.
Report an issue: GitHub.