chenhg5/cc-connect · error

dingtalk: proactive send request

Error message

dingtalk: proactive send request: %w

What it means

The HTTP transport layer failed while POSTing the proactive message to the DingTalk API. This wraps the client-side network error (DNS failure, connection refused, TLS error, timeout) returned by p.httpClient.Do(req), before any HTTP status is received.

Solutions

  1. Check basic connectivity to the DingTalk API host (curl https://api.dingtalk.com) from the host running cc-connect.
  2. If a proxy is configured in the dingtalk platform options, verify the proxy URL, credentials, and that the proxy is reachable.
  3. Inspect the wrapped error (%w): context deadline exceeded → increase the HTTP client timeout; connection refused/proxy → fix network or proxy settings.
  4. Add retry with backoff for transient network failures on this idempotent send path.

Example fix

// before
err := p.Send(ctx, rc, msg) // fires once, no timeout budget
// after
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := p.Send(ctx, rc, msg)
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", "api.dingtalk.com:443", 3*time.Second)
if err != nil {
    slog.Warn("dingtalk unreachable", "err", err)
} else {
    conn.Close()
}

Try / catch

var nerr net.Error
if errors.As(err, &nerr) && nerr.Timeout() {
    // retry with backoff
}
if err := p.Send(ctx, rc, msg); err != nil {
    slog.Warn("proactive send transport error", "err", err)
}

Prevention

When it happens

Trigger: p.httpClient.Do(req) returns a non-nil error for the proactive-send POST — network unreachable, DNS resolution failure, proxy failure, or context deadline exceeded mid-request.

Common situations: No internet access or blocked access to api.dingtalk.com (firewall/corporate proxy); proxy misconfiguration in the platform options; request timeout set too low; DingTalk service outage.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/b8fac34a41df4f34. Report an issue: GitHub.

Appendix: source

Thrown at platform/dingtalk/dingtalk.go:1692

	} else {
		return fmt.Errorf("dingtalk: proactive send requires conversationId (group) or senderStaffId (direct)")
	}

	body, err := json.Marshal(requestBody)
	if err != nil {
		return fmt.Errorf("dingtalk: marshal proactive message: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("dingtalk: create proactive request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-acs-dingtalk-access-token", token)

	resp, err := p.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("dingtalk: proactive send request: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	respBody, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("dingtalk: proactive send failed: status=%d, body=%s", resp.StatusCode, string(respBody))
	}

	slog.Debug("dingtalk: proactive message sent", "api", apiURL, "status", resp.StatusCode)
	return nil
}

var atUserIDRegexp = regexp.MustCompile(`@(\d{4,})`)

// extractAtUserIds extracts @userId patterns from content for DingTalk's atUserIds field.
// Matches @ followed by numeric DingTalk user IDs (e.g. @194252073827812352).
func extractAtUserIds(content string) []string {
	matches := atUserIDRegexp.FindAllStringSubmatch(content, -1)

View on GitHub (pinned to 4000b2338a)