chenhg5/cc-connect · warning

%s: no tracked card messageID for session %q

Error message

%s: no tracked card messageID for session %q

What it means

feishu: no tracked card messageID for session is raised in RefreshCard when there is no recorded message ID for the given session key, so the existing card cannot be patched in place. The adapter tracks interactive-card message IDs (from card action callbacks) to update cards later. Without a tracked ID, refresh is impossible and a fresh send is the fallback.

Source

Thrown at platform/feishu/card.go:64

	if !p.noReplyToTrigger && p.shouldReplyInThread(rc) {
		return p.ReplyCard(ctx, rctx, card)
	}

	cardJSON := renderCard(card, rc.sessionKey)
	return p.createMessage(ctx, rc.chatID, larkim.MsgTypeInteractive, cardJSON, "send card")
}

// RefreshCard updates a previously rendered card in-place using the Patch API.
// It looks up the messageID stored from the most recent card action callback
// for the given session key and patches that message with the new card content.
func (p *interactivePlatform) RefreshCard(ctx context.Context, sessionKey string, card *core.Card) error {
	p.cardActionMsgMu.Lock()
	msgID := p.cardActionMsgIDs[sessionKey]
	p.cardActionMsgMu.Unlock()

	if msgID == "" {
		return fmt.Errorf("%s: no tracked card messageID for session %q", p.tag(), sessionKey)
	}

	cardJSON := renderCard(card, sessionKey)
	req := larkim.NewPatchMessageReqBuilder().
		MessageId(msgID).
		Body(larkim.NewPatchMessageReqBodyBuilder().
			Content(cardJSON).
			Build()).
		Build()
	return p.withTransientRetry(ctx, "refresh card", func() error {
		return p.withFreshTenantAccessTokenRetry(ctx, "refresh card", func(client *lark.Client, options ...larkcore.RequestOptionFunc) error {
			resp, err := client.Im.Message.Patch(ctx, req, options...)
			if err != nil {
				return fmt.Errorf("%s: refresh card: %w", p.tag(), err)
			}
			if !resp.Success() {
				return fmt.Errorf("%s: refresh card code=%d msg=%s", p.tag(), resp.Code, resp.Msg)
			}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Fall back to SendCard with the session's chat ID when RefreshCard reports no tracked message
  2. Persist cardActionMsgIDs across restarts if refresh must survive restarts
  3. Verify the sessionKey matches the one used in the card action callback
  4. Ensure the original message was sent as an interactive card (only those get tracked)

Example fix

// before
err := p.RefreshCard(ctx, sessionKey, card)
// after
if err := p.RefreshCard(ctx, sessionKey, card); err != nil {
    if rc, ok := sessionReplyCtx(sessionKey); ok {
        err = p.SendCard(ctx, rc, card) // fallback: fresh message
    }
}
Defensive patterns

Strategy: fallback

Validate before calling

p.cardActionMsgMu.Lock(); _, tracked := p.cardActionMsgIDs[sessionKey]; p.cardActionMsgMu.Unlock()
if !tracked { return errors.New("no tracked card; use SendCard instead") }

Try / catch

if err := p.RefreshCard(ctx, sessionKey, card); err != nil {
    log.Warn("card refresh unavailable; sending fresh card", "err", err)
    return p.SendCard(ctx, rc, card)
}

Prevention

When it happens

Trigger: RefreshCard (card.go:64) called with a sessionKey never seen in a card action callback, a typo'd session key, or after adapter restart cleared the in-memory cardActionMsgIDs map.

Common situations: Bot restarted between card render and refresh (map is memory-only); user clicked a card from a different session than expected; card was not interactive so no message ID was ever recorded.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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