chenhg5/cc-connect · critical
dingtalk: get access token for proactive send: %w
Error message
dingtalk: get access token for proactive send: %w
What it means
sendProactiveMessage — used by cc-connect send, cron, webhook and other proactive features — first obtains a DingTalk access token, and this error wraps any failure from getAccessToken. Unlike passive replies (which reuse the sessionWebhook), proactive sending requires the group/direct message API, which requires a valid app access_token. Without it, no proactive message can be sent.
Source
Thrown at platform/dingtalk/dingtalk.go:1646
if len(parts) > 2 {
senderStaffId = parts[2]
}
return replyContext{
conversationId: conversationId,
senderStaffId: senderStaffId,
isGroup: convType == "g",
proactive: true,
}, nil
}
// sendProactiveMessage sends a message using the DingTalk group/direct message API
// instead of the temporary sessionWebhook. This enables cc-connect send, cron,
// webhook, and other proactive messaging features.
func (p *Platform) sendProactiveMessage(ctx context.Context, rc replyContext, content string) error {
token, err := p.getAccessToken()
if err != nil {
return fmt.Errorf("dingtalk: get access token for proactive send: %w", err)
}
content = preprocessDingTalkMarkdown(content)
var apiURL string
var requestBody map[string]any
if rc.isGroup && rc.conversationId != "" {
// Group message via /v1.0/robot/groupMessages/send
apiURL = "https://api.dingtalk.com/v1.0/robot/groupMessages/send"
msgParam, _ := json.Marshal(map[string]string{"text": content})
requestBody = map[string]any{
"robotCode": p.robotCode,
"openConversationId": rc.conversationId,
"msgKey": "sampleMarkdown",
"msgParam": string(msgParam),
}
} else if rc.senderStaffId != "" {View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the wrapped inner error (%w) — it distinguishes credential rejection vs. network failure vs. API error response.
- Verify appKey and appSecret in config.toml match an active app in the DingTalk Open Platform developer console.
- Test connectivity: curl the token endpoint from the host running cc-connect to rule out firewall/DNS/proxy issues.
- Confirm the app has the group chat / bot message permissions (qyapi scoped) required for proactive sending.
- Restart cc-connect after fixing credentials so cached state resets, then re-run the proactive send.
Example fix
// before
token, err := p.getAccessToken()
if err != nil {
return fmt.Errorf("dingtalk: get access token for proactive send: %w", err)
}
// after
token, err := p.getAccessToken()
if err != nil {
slog.Error("dingtalk: proactive send aborted, token acquisition failed", "err", err)
return fmt.Errorf("dingtalk: get access token for proactive send: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// before a proactive send, confirm credentials exist and endpoint is reachable
if p.appKey == "" || p.appSecret == "" {
return fmt.Errorf("dingtalk appKey/appSecret missing in config")
}
resp, err := http.Post(p.tokenURL(), "application/json", strings.NewReader("{}"))
if err != nil {
return fmt.Errorf("token endpoint unreachable: %w", err)
}
_ = resp.Body.Close() Type guard
func canProactivelySend(p *Platform) bool {
return p != nil && p.appKey != "" && p.appSecret != ""
} Try / catch
err := platform.SendProactive(ctx, sessionKey, text)
if err != nil {
if strings.Contains(err.Error(), "get access token for proactive send") {
// wait and retry once; token issues are often transient or fixed by config
time.Sleep(5 * time.Second)
err = platform.SendProactive(ctx, sessionKey, text)
}
if err != nil {
slog.Error("proactive send failed", "err", err)
return err
}
} Prevention
- Verify appKey/appSecret in config.toml against the active DingTalk app.
- Confirm egress to oapi.dingtalk.com is allowed by firewalls/proxies.
- Keep required bot/group-message permissions enabled in the developer console.
- Refresh tokens proactively before expiry; avoid reusing stale cached tokens.
- Restart cc-connect after credential changes to clear cached auth state.
When it happens
Trigger: Any proactive send (CLI send command, cron job, webhook trigger) when getAccessToken fails: wrong appKey/appSecret in config, DingTalk token endpoint unreachable (network/DNS), token endpoint returning an error payload, or cached token expired and refresh failed.
Common situations: appKey/appSecret typo or credentials from a different/removed app in config.toml; enterprise firewall blocking egress to oapi.dingtalk.com; DingTalk app disabled or permissions revoked in the developer console; clock skew invalidating token caching logic.
Related errors
- get access token: %w
- get access token: %w
- dingtalk: get access token for emotion: %w
- dingtalk: get access token: %w
- dingtalk: proactive send failed: status=%d, body=%s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/7ba2ef01cdddd638.
Report an issue: GitHub.