{"record":{"id":"4a424ed8a6038dba","repo":"chenhg5/cc-connect","slug":"dingtalk-card-api-temporarily-degraded","errorCode":null,"errorMessage":"dingtalk: card API temporarily degraded","messagePattern":"dingtalk: card API temporarily degraded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1089,"sourceCode":"\n\tslog.Info(\"dingtalk: image message sent\", \"media_id\", mediaID, \"user\", rc.senderStaffId)\n\treturn nil\n}\n\nvar _ core.ImageSender = (*Platform)(nil)\nvar _ core.StreamingCardPlatform = (*Platform)(nil)\nvar _ core.ReplyContextReconstructor = (*Platform)(nil)\nvar _ core.TypingIndicator = (*Platform)(nil)\nvar _ core.TypingIndicatorDone = (*Platform)(nil)\n\n// CreateStreamingCard creates a new streaming card for the given reply context.\n// Implements core.StreamingCardPlatform.\nfunc (p *Platform) CreateStreamingCard(ctx context.Context, replyCtx any) (core.StreamingCard, error) {\n\tif p.cardTemplateID == \"\" {\n\t\treturn nil, fmt.Errorf(\"dingtalk: card_template_id not configured\")\n\t}\n\tif p.isCardDegraded() {\n\t\treturn nil, fmt.Errorf(\"dingtalk: card API temporarily degraded\")\n\t}\n\trc, ok := replyCtx.(replyContext)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"dingtalk: invalid reply context type %T\", replyCtx)\n\t}\n\treturn p.createAICard(ctx, rc)\n}\n\n// SendFile uploads and sends a file via DingTalk oToMessages API.\n// Implements core.FileSender.\nfunc (p *Platform) SendFile(ctx context.Context, rctx any, file core.FileAttachment) error {\n\trc, ok := rctx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"dingtalk: SendFile: invalid reply context type %T\", rctx)\n\t}\n\n\tname := file.FileName\n\tif name == \"\" {","sourceCodeStart":1071,"sourceCodeEnd":1107,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1071-L1107","documentation":"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.","triggerScenarios":"Prior createAICard/stream/doStream calls failed repeatedly (token errors, 4xx/5xx from card API), tripping the degradation flag; error self-clears after the cooldown.","commonSituations":"DingTalk card service outage or sustained 429s; misconfigured card credentials causing every create to fail until the breaker opens.","solutions":["Wait for the degradation cooldown to elapse and retry","Fix the underlying card API failures (check earlier logs for createAICard errors)","Verify card-related credentials/permissions in DingTalk open platform","Let the engine fall back to plain-text replies while degraded — this is expected behavior"],"exampleFix":"// before\nif err := p.SendImage(ctx, rc, img); err != nil { log.Fatal(err) }\n// after\nif _, err := p.CreateStreamingCard(ctx, rc); err != nil && strings.Contains(err.Error(), \"degraded\") {\n    // platform will fall back to plain text; not fatal\n    return\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"card, err := p.CreateStreamingCard(ctx, rc)\nif err != nil {\n    // degraded or otherwise unavailable: fall back to plain text streaming\n    card = nil\n    slog.Warn(\"streaming card unavailable, using text fallback\", \"err\", err)\n}","preventionTips":["Treat degradation as transient — always implement a plain-text fallback path","Fix root-cause card API failures logged before the breaker opened","Monitor degradation frequency; repeated trips indicate credential or quota problems"],"tags":["dingtalk","streaming-cards","circuit-breaker","fallback"],"backgroundTag":"upstream-api-error","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}