chenhg5/cc-connect · error

%s: StreamRichCardText: invalid preview handle type %T

Error message

%s: StreamRichCardText: invalid preview handle type %T

What it means

StreamRichCardText implements core.RichCardTextStreamer and accepts the preview handle as `any`. If the caller passes anything that is not a *feishuPreviewHandle (e.g. a handle from a different platform or a nil/wrong type), the type assertion fails and this error is thrown. It is a programming/contract error, not an API error.

Source

Thrown at platform/feishu/feishu.go:5209

	}
	if resp.Data.CardID == "" {
		return "", fmt.Errorf("%s: create card entity: empty card_id in response", p.tag())
	}
	return resp.Data.CardID, nil
}

// StreamRichCardText implements core.RichCardTextStreamer. Pushes the latest
// fullText to the rich card's main_text element via cardkit-v1 streaming text
// update API. The Lark client renders the increment between consecutive PUTs
// with a typewriter animation (controlled by the card's streaming_config).
//
// Returns ErrNotSupported when the handle has no cardID (preview was created
// via the inline-card-JSON fallback path; engine should fall back to full-card
// Patch).
func (p *Platform) StreamRichCardText(ctx context.Context, previewHandle any, fullText string) error {
	h, ok := previewHandle.(*feishuPreviewHandle)
	if !ok {
		return fmt.Errorf("%s: StreamRichCardText: invalid preview handle type %T", p.tag(), previewHandle)
	}

	// Serialize all PUTs for one card so the monotonic sequence counter is
	// preserved across concurrent EventText calls; rate-limit headroom is
	// huge (Lark allows 50 QPS per element).
	h.mu.Lock()
	defer h.mu.Unlock()

	if h.cardID == "" {
		return core.ErrNotSupported
	}

	h.sequence++
	apiPath := fmt.Sprintf("/open-apis/cardkit/v1/cards/%s/elements/%s/content",
		h.cardID, richCardMainTextElementID)
	body := map[string]any{
		"content":  fullText,
		"sequence": h.sequence,

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Only pass the exact handle returned by the same Feishu platform's SendPreview/preview-creation call
  2. Check core.ErrNotSupported separately: a feishu handle with empty CardID legitimately returns ErrNotSupported, not this error
  3. Fix engine dispatch so handles are always paired with the platform that created them
  4. In tests, construct handles as *feishuPreviewHandle

Example fix

// before
err := platform.StreamRichCardText(ctx, someOtherHandle, text)
// after
if h, ok := someOtherHandle.(*feishu.PreviewHandle); ok {
	err = platform.StreamRichCardText(ctx, h, text)
} else {
	err = core.ErrNotSupported // or fall back to full-card Patch
}
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := previewHandle.(*feishuPreviewHandle); !ok {
	// do not call StreamRichCardText; use full-card Patch instead
}

Type guard

func asFeishuHandle(h any) (*feishuPreviewHandle, bool) {
	p, ok := h.(*feishuPreviewHandle)
	return p, ok
}

Try / catch

if err := p.StreamRichCardText(ctx, handle, text); err != nil {
	if errors.Is(err, core.ErrNotSupported) || strings.Contains(err.Error(), "invalid preview handle type") {
		// fall back to full-card Patch
	}
}

Prevention

When it happens

Trigger: Calling StreamRichCardText with a handle obtained from another platform adapter, a value (non-pointer) feishuPreviewHandle, a nil interface, or a handle from the inline-card-JSON fallback path that was stored with the wrong concrete type.

Common situations: Engine wired up to a mixed set of platforms and passes one platform's handle to another; refactoring changed the handle type; test code constructing ad-hoc handles.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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