chenhg5/cc-connect · warning

dingtalk: emotion returned success=false

Error message

dingtalk: emotion returned success=false

What it means

This error is returned by sendEmotion when DingTalk's emotion API returns HTTP 200 but the JSON body contains success=false, meaning DingTalk accepted the request at the transport layer but refused/refused-to-apply the emotion business operation. DingTalk reports some business failures (e.g. target message no longer exists, robot cannot reply to that message) inside a 200 envelope rather than an HTTP error status, so the library inspects the success field explicitly.

Source

Thrown at platform/dingtalk/dingtalk.go:976

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

	respBody, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("dingtalk: emotion returned status %d: %s", resp.StatusCode, string(respBody))
	}
	if len(respBody) == 0 {
		return nil
	}
	var result struct {
		Success *bool `json:"success"`
	}
	if err := json.Unmarshal(respBody, &result); err == nil && result.Success != nil && !*result.Success {
		return fmt.Errorf("dingtalk: emotion returned success=false")
	}
	return nil
}

// StartTyping adds a DingTalk emotion to the user's message while the agent is processing.
func (p *Platform) StartTyping(ctx context.Context, rctx any) (stop func()) {
	rc, ok := rctx.(replyContext)
	if !ok || p.reactionEmoji == "" || rc.messageID == "" || rc.conversationId == "" {
		return func() {}
	}
	if err := p.sendEmotion(ctx, rc, p.reactionEmoji, false); err != nil {
		slog.Debug("dingtalk: add typing emotion failed", "error", err)
	}
	return func() {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		if err := p.sendEmotion(ctx, rc, p.reactionEmoji, true); err != nil {
			slog.Debug("dingtalk: recall typing emotion failed", "error", err)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Treat as non-fatal if it's the typing indicator (StartTyping) — it is cosmetic and safe to ignore
  2. Verify the robot app has emotion reply enabled for your org/conversation type in the DingTalk console
  3. Check whether the target messageId still exists; don't react to withdrawn/expired messages
  4. Capture the full response body (enable debug logging) to see any accompanying errcode
  5. Update the DingTalk app/robot to the latest version if emotion features were recently added

Example fix

// before: unconditionally reacting to every inbound message
p.AddDoneReaction(ctx, msg)
// after: skip if the message was withdrawn or is too old
if msg.Withdrawn || time.Since(msg.Timestamp) > maxEmotionAge { return nil }
return p.AddDoneReaction(ctx, msg)
Defensive patterns

Strategy: fallback

Validate before calling

// Go: skip reaction if the target message is gone or too old
if msg.Withdrawn || time.Since(msg.Timestamp) > 10*time.Minute {
    return nil
}

Try / catch

if err := p.AddDoneReaction(ctx, msg); err != nil {
    if strings.Contains(err.Error(), "success=false") {
        // business refusal, not a bug — degrade gracefully
        slog.Debug("dingtalk emotion not applied", "msg", msg.ID)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: POST to /v1.0/robot/emotion/reply or /robot/emotion/recall returns 200 with {"success":false} — typically the target message is too old or deleted, the robot lacks emotion-reply rights for that conversation, or the recall targeted an emotion that no longer exists.

Common situations: AddDoneReaction fired after the user's message was withdrawn; StartTyping racing message deletion; robot app not upgraded to support emotion reply in that org; DingTalk quirk returning success=false for unsupported conversation types.

Related errors


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