chenhg5/cc-connect · warning

dingtalk: card API temporarily degraded

Error message

dingtalk: card API temporarily degraded

What it means

CreateStreamingCard refuses to create a card while the DingTalk card API is marked degraded — an internal circuit-breaker that trips after repeated card API failures so streaming output falls back to plain text instead of hammering a failing API.

Solutions

  1. Wait for the degradation cooldown to elapse and retry
  2. Fix the underlying card API failures (check earlier logs for createAICard errors)
  3. Verify card-related credentials/permissions in DingTalk open platform
  4. Let the engine fall back to plain-text replies while degraded — this is expected behavior

Example fix

// before
if err := p.SendImage(ctx, rc, img); err != nil { log.Fatal(err) }
// after
if _, err := p.CreateStreamingCard(ctx, rc); err != nil && strings.Contains(err.Error(), "degraded") {
    // platform will fall back to plain text; not fatal
    return
}
Defensive patterns

Strategy: fallback

Try / catch

card, err := p.CreateStreamingCard(ctx, rc)
if err != nil {
    // degraded or otherwise unavailable: fall back to plain text streaming
    card = nil
    slog.Warn("streaming card unavailable, using text fallback", "err", err)
}

Prevention

When it happens

Trigger: Prior createAICard/stream/doStream calls failed repeatedly (token errors, 4xx/5xx from card API), tripping the degradation flag; error self-clears after the cooldown.

Common situations: DingTalk card service outage or sustained 429s; misconfigured card credentials causing every create to fail until the breaker opens.

Related errors


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

Appendix: source

Thrown at platform/dingtalk/dingtalk.go:1089

	slog.Info("dingtalk: image message sent", "media_id", mediaID, "user", rc.senderStaffId)
	return nil
}

var _ core.ImageSender = (*Platform)(nil)
var _ core.StreamingCardPlatform = (*Platform)(nil)
var _ core.ReplyContextReconstructor = (*Platform)(nil)
var _ core.TypingIndicator = (*Platform)(nil)
var _ core.TypingIndicatorDone = (*Platform)(nil)

// CreateStreamingCard creates a new streaming card for the given reply context.
// Implements core.StreamingCardPlatform.
func (p *Platform) CreateStreamingCard(ctx context.Context, replyCtx any) (core.StreamingCard, error) {
	if p.cardTemplateID == "" {
		return nil, fmt.Errorf("dingtalk: card_template_id not configured")
	}
	if p.isCardDegraded() {
		return nil, fmt.Errorf("dingtalk: card API temporarily degraded")
	}
	rc, ok := replyCtx.(replyContext)
	if !ok {
		return nil, fmt.Errorf("dingtalk: invalid reply context type %T", replyCtx)
	}
	return p.createAICard(ctx, rc)
}

// SendFile uploads and sends a file via DingTalk oToMessages API.
// Implements core.FileSender.
func (p *Platform) SendFile(ctx context.Context, rctx any, file core.FileAttachment) error {
	rc, ok := rctx.(replyContext)
	if !ok {
		return fmt.Errorf("dingtalk: SendFile: invalid reply context type %T", rctx)
	}

	name := file.FileName
	if name == "" {

View on GitHub (pinned to 4000b2338a)