chenhg5/cc-connect · error
stream AI card: status=%d, body=%s
Error message
stream AI card: status=%d, body=%s
What it means
DingTalk's /v1.0/card/streaming endpoint returned a non-200 status for a card content update (platform/dingtalk/card.go:377, doStream). The error includes the status code and response body. For statuses 403, 429, and >=500, the card is additionally marked failed (state="failed", done channel closed) and activateCardDegrade switches the platform to plain-text replies.
Source
Thrown at platform/dingtalk/card.go:377
slog.Debug("dingtalk: streaming response",
"status", resp.StatusCode,
"body", string(respBody),
"isFinalize", isFinalize)
if resp.StatusCode != http.StatusOK {
slog.Error("dingtalk: stream AI card failed",
"status", resp.StatusCode,
"body", string(respBody))
// Check if we should trigger degrade
if resp.StatusCode == 403 || resp.StatusCode == 429 || resp.StatusCode >= 500 {
c.platform.activateCardDegrade(fmt.Sprintf("card.stream:%d", resp.StatusCode))
c.mu.Lock()
c.state = "failed"
close(c.done)
c.mu.Unlock()
}
return fmt.Errorf("stream AI card: status=%d, body=%s", resp.StatusCode, string(respBody))
}
slog.Debug("dingtalk: AI card streamed successfully", "isFinalize", isFinalize)
return nil
}
// Finalize sends the final content and marks the card as complete.
func (c *aiCard) Finalize(ctx context.Context, content string) error {
c.mu.Lock()
// Stop any pending timer
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
// If already finished or failed, skip
if c.state == "finished" || c.state == "failed" {View on GitHub (pinned to 4000b2338a)
Solutions
- Read the embedded status and body — DingTalk returns a JSON code identifying the cause
- On 401: ensure getAccessToken refreshes expired tokens; verify appKey/appSecret are correct
- On 429: increase cardThrottleMs to lower streaming request rate; degrade mode activates automatically
- On 404/400: the card instance is gone — abandon the card and send the content as a normal text message instead
- On 5xx: retry; once the card state is "failed", Update/Finalize become no-ops, so fall back to a plain reply
- Check the DingTalk open platform console for card template and streaming-API permission issues on 403
Example fix
// before cardThrottleMs = 200 // may exceed QPS limits // after cardThrottleMs = 1000 // reduce streaming request rate to avoid 429
Defensive patterns
Strategy: fallback
Validate before calling
// verify token validity and template key before streaming
if _, err := c.platform.getAccessToken(context.Background()); err != nil {
return fmt.Errorf("skip streaming; token invalid: %w", err)
}
if c.templateKey == "" {
return fmt.Errorf("cardTemplateKey not configured; streaming updates will be rejected")
} Try / catch
if err := card.Finalize(ctx, content); err != nil && strings.Contains(err.Error(), "status=") {
switch {
case strings.Contains(err.Error(), "status=401"):
// refresh token and retry once
case strings.Contains(err.Error(), "status=429"):
// back off; raise throttle interval
default:
// card may be failed/gone: send plain text message instead
p.Reply(ctx, msg, content)
}
} Prevention
- Set cardThrottleMs high enough to stay under DingTalk QPS limits
- Refresh access tokens before expiry so long streams don't hit 401
- Don't finalize or delete cards concurrently with streaming updates
- Handle card failure (state=failed) by switching to plain-text replies for the remainder of the session
When it happens
Trigger: PUT /v1.0/card/streaming returns non-200: expired/invalid access token (401), streaming permission denied or card already finalized (403), unknown outTrackId / card instance no longer exists (400/404), QPS throttling (429), or DingTalk server errors (5xx).
Common situations: Streaming continues after the token expired mid-card (401); card was deleted or conversation disbanded while streaming (404); sending final updates after the card was already finalized elsewhere; exceeding API QPS with aggressive throttle settings (429); DingTalk incident (5xx).
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- create AI card: status=%d, body=%s
- AI card delivery failed: %s
- create request: %w
- do request: %w
- api returned status %d
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/761ed46728c19fd1.
Report an issue: GitHub.