chenhg5/cc-connect · warning

%s: stream rich card text: parse response: %w

Error message

%s: stream rich card text: parse response: %w

What it means

The streaming element-content PUT returned HTTP 200 but the body could not be unmarshaled into {code,msg}. The frame's success cannot be determined, so the code errors out. Usually means the body is not Cardkit JSON — proxy page, empty body, or an unexpected envelope.

Source

Thrown at platform/feishu/feishu.go:5246

	}

	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)
	}
	return nil
}

// UpdateMessage edits an existing card message identified by previewHandle.
// Uses the Patch API (HTTP PATCH) which is required for interactive card messages.
func (p *Platform) UpdateMessage(ctx context.Context, previewHandle any, content string) error {
	if !p.useInteractiveCard {
		return core.ErrNotSupported
	}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Log string(apiResp.RawBody) when this fires to capture the actual body
  2. Verify no proxy/CDN sits between the host and open.feishu.cn
  3. Update the inline struct if Feishu changed the response shape
  4. Since frames are best-effort, consider downgrading to a warning and continuing the stream

Example fix

// before
if err := json.Unmarshal(apiResp.RawBody, &resp); err != nil {
	return fmt.Errorf("%s: stream rich card text: parse response: %w", p.tag(), err)
}
// after
if err := json.Unmarshal(apiResp.RawBody, &resp); err != nil {
	slog.Warn(p.tag()+": stream rich card text: unparseable response; continuing", "err", err, "raw", string(apiResp.RawBody))
	return nil // frames are best-effort; final Patch converges
}
Defensive patterns

Strategy: try-catch

Validate before calling

if apiResp == nil || len(apiResp.RawBody) == 0 {
	// skip frame; final Patch will converge
}

Try / catch

if err := p.StreamRichCardText(ctx, h, text); err != nil {
	if strings.Contains(err.Error(), "parse response") {
		slog.Debug("unparseable stream frame response; skipping frame")
		// continue streaming
	}
}

Prevention

When it happens

Trigger: json.Unmarshal(apiResp.RawBody, &resp) fails on the PUT response: empty RawBody, HTML from an intercepting proxy, or a response whose code field is not an integer (schema drift).

Common situations: Corporate proxy injecting pages; Feishu returning an undocumented envelope for this endpoint in some regions; truncated responses under heavy streaming load.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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