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

  1. Check the errcode against WeCom docs: 40001 → fix corpsecret; 40013 → fix corpid; 60020 → add server IP to trusted IPs.
  2. Verify the corpid and corpsecret in the channel credentials match the WeCom admin console for the correct app.
  3. If credentials were rotated, update them and clear the adapter's tokenCache.
  4. 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

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


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/548e5b4e5af3b2e8. Report an issue: GitHub.