chenhg5/cc-connect · warning
%s: stream rich card text: %w
Error message
%s: stream rich card text: %w
What it means
Wraps a transport/client error from withFreshTenantAccessTokenRetry around the PUT /open-apis/cardkit/v1/cards/{card_id}/elements/{element_id}/content streaming-update call. Any network, token, or context failure during a streaming frame lands here, wrapped with %w so the original cause is preserved.
Source
Thrown at platform/feishu/feishu.go:5236
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,
}
var apiResp *larkcore.ApiResp
if err := p.withFreshTenantAccessTokenRetry(ctx, "stream rich card text", func(client *lark.Client, options ...larkcore.RequestOptionFunc) error {
var err error
apiResp, err = client.Put(ctx, apiPath, body, larkcore.AccessTokenTypeTenant, options...)
return err
}); err != nil {
return fmt.Errorf("%s: stream rich card text: %w", p.tag(), err)
}
if apiResp == nil || apiResp.StatusCode != http.StatusOK {
return fmt.Errorf("%s: stream rich card text: HTTP status %d", p.tag(), apiResp.StatusCode)
}
var resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
if err := json.Unmarshal(apiResp.RawBody, &resp); err != nil {
return fmt.Errorf("%s: stream rich card text: parse response: %w", p.tag(), err)
}
if resp.Code != 0 {
err := classifyFeishuCardAPIError("stream rich card text", resp.Code, resp.Msg)
if errors.Is(err, errFeishuCardRateLimited) {
slog.Debug(p.tag()+": stream rich card text rate limited; skipping frame", "code", resp.Code)
return nil
}
return fmt.Errorf("%s: %w", p.tag(), err)View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the wrapped inner error to identify network vs auth vs timeout
- Since this is a streaming frame, treat as best-effort: log and let the next frame or the final full-card Patch converge the content
- Verify token/credentials if the error persists across frames
- Increase frame timeout or add small backoff between frames on transient failures
Defensive patterns
Strategy: retry
Validate before calling
if h.cardID == "" {
return core.ErrNotSupported // don't attempt streaming without an entity
}
if err := ctx.Err(); err != nil { return err } Try / catch
if err := p.StreamRichCardText(ctx, h, text); err != nil {
slog.Debug("stream frame failed; will converge on final patch", "err", err)
// frames are incremental: skip and continue
} Prevention
- Treat individual frames as best-effort; the final UpdateMessage Patch carries full text
- Use per-frame timeouts rather than one long deadline
- Add small backoff after consecutive transport failures
- Keep the final full-card Patch path always enabled
When it happens
Trigger: client.Put returns an error: network interruption mid-stream, tenant token refresh failure, context deadline exceeded (streaming frame took too long), or the card was deleted making the request fail at transport/auth level.
Common situations: Flaky network between the host and Feishu during typewriter streaming; very long agent turns exceeding the frame ctx deadline; app credentials rotated mid-session so fresh-token acquisition fails; Feishu-side incidents.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- %s: create card entity: %w
- %s: stream rich card text: HTTP status %d
- %s: stream rich card text: parse response: %w
- redirected to unsupported image URL
- remote image host resolved to no usable IPs
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/240e450c18c8d3ee.
Report an issue: GitHub.