chenhg5/cc-connect · error

AI card delivery failed: %s

Error message

AI card delivery failed: %s

What it means

DingTalk returned HTTP 200 but the response's deliverResults array contains an entry with success=false, meaning the card instance was created but could not actually be delivered to the conversation (platform/dingtalk/card.go:198, createAICard). The DingTalk per-delivery errorMsg is embedded in the error. Unlike transport failures, this is an application-level rejection by DingTalk's IM delivery pipeline.

Source

Thrown at platform/dingtalk/card.go:198

	}

	// Check deliverResults for actual delivery success
	var deliverCheck struct {
		Result struct {
			DeliverResults []struct {
				Success  bool   `json:"success"`
				ErrorMsg string `json:"errorMsg"`
			} `json:"deliverResults"`
		} `json:"result"`
	}
	if err := json.Unmarshal(respBody, &deliverCheck); err == nil {
		for _, dr := range deliverCheck.Result.DeliverResults {
			if !dr.Success {
				slog.Warn("dingtalk: AI card delivery failed",
					"errorMsg", dr.ErrorMsg,
					"outTrackId", outTrackId,
					"isGroup", isGroup)
				return nil, fmt.Errorf("AI card delivery failed: %s", dr.ErrorMsg)
			}
		}
	}

	slog.Info("dingtalk: AI card created",
		"cardInstanceId", cardInstanceId,
		"outTrackId", resolvedOutTrackId)

	card := &aiCard{
		cardInstanceId: cardInstanceId,
		outTrackId:     resolvedOutTrackId,
		templateKey:    p.cardTemplateKey,
		platform:       p,
		state:          "processing",
		throttleMs:     p.cardThrottleMs,
		done:           make(chan struct{}),
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the embedded errorMsg — it names the delivery failure reason
  2. For groups: re-add the robot to the target group chat and confirm the robot's app scope includes that org/users
  3. For 1:1 chats: confirm the user hasn't blocked the robot and is inside the app's visible range
  4. Verify conversationId/senderStaffId come from the current incoming message, not a stale cached session
  5. Check robotCode in config matches the robot actually delivering messages in that conversation
Defensive patterns

Strategy: fallback

Validate before calling

// ensure robot can deliver before starting a card flow
// robot must be a member of the group and within app visibility scope
if rc.isGroup && !robotInGroup(rc.conversationId, p.robotCode) {
	return fmt.Errorf("robot %s not in group %s; delivery will fail", p.robotCode, rc.conversationId)
}

Try / catch

card, err := p.CreateStreamingCard(ctx, msg)
if err != nil && strings.Contains(err.Error(), "AI card delivery failed:") {
	// DingTalk rejected delivery: fall back to plain text reply
	p.Reply(ctx, msg, content)
}

Prevention

When it happens

Trigger: The createAndDeliver response contains result.deliverResults[i] with success=false and a non-empty errorMsg, e.g. when the robot is not a member of the target group, the target user has blocked the robot, or the openSpaceId (derived from conversationId/senderStaffId) doesn't match a live conversation.

Common situations: Robot removed from or never added to the group chat; 1:1 chat where the user blocked/disabled the robot; stale conversationId from a session after the chat was disbanded; robotCode mismatch with the robot actually in the conversation; user not visible to the app's visibility scope.

Related errors


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