chenhg5/cc-connect · error

%s: create card entity: HTTP status %d

Error message

%s: create card entity: HTTP status %d

What it means

The cardkit Create Card Entity API completed at the transport level but returned a non-200 HTTP status (or a nil response). The code checks apiResp.StatusCode != http.StatusOK before parsing the body, so gateway errors, auth rejections (401/403), and rate-limit responses surface here rather than as a Lark business code.

Source

Thrown at platform/feishu/feishu.go:5177

// (PUT /open-apis/cardkit/v1/cards/{card_id}/elements/{element_id}/content).
// If this call fails the caller should fall back to inline card JSON via the
// regular Im.Message.Create path; the rich card will still render but without
// native typewriter streaming.
func (p *Platform) createCardEntity(ctx context.Context, cardJSON string) (string, error) {
	body := map[string]any{
		"type": "card_json",
		"data": cardJSON,
	}
	var apiResp *larkcore.ApiResp
	if err := p.withFreshTenantAccessTokenRetry(ctx, "create card entity", func(client *lark.Client, options ...larkcore.RequestOptionFunc) error {
		var err error
		apiResp, err = client.Post(ctx, "/open-apis/cardkit/v1/cards", body, larkcore.AccessTokenTypeTenant, options...)
		return err
	}); err != nil {
		return "", fmt.Errorf("%s: create card entity: %w", p.tag(), err)
	}
	if apiResp == nil || apiResp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("%s: create card entity: HTTP status %d", p.tag(), apiResp.StatusCode)
	}
	var resp struct {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
		Data struct {
			CardID string `json:"card_id"`
		} `json:"data"`
	}
	if err := json.Unmarshal(apiResp.RawBody, &resp); err != nil {
		return "", fmt.Errorf("%s: create card entity: parse response: %w", p.tag(), err)
	}
	if resp.Code != 0 {
		return "", fmt.Errorf("%s: %w", p.tag(), classifyFeishuCardAPIError("create card entity", resp.Code, resp.Msg))
	}
	if resp.Data.CardID == "" {
		return "", fmt.Errorf("%s: create card entity: empty card_id in response", p.tag())
	}
	return resp.Data.CardID, nil

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the logged status code: 401/403 → fix app credentials or grant cardkit:card permission in the Feishu developer console; 5xx → retry later
  2. Confirm the app version containing the Cardkit permission is published/released (permissions must be released, not just enabled)
  3. Reduce the card JSON size (trim schema/whitespace) if 413
  4. Check proxy/firewall interference if statuses like 502 appear
Defensive patterns

Strategy: fallback

Validate before calling

// pre-flight: verify a cheap cardkit call works at startup
// e.g. doctor check that POST /open-apis/cardkit/v1/cards is authorized

Try / catch

if err != nil {
	var httpErr *HTTPStatusError // or match on the wrapped message
	if errors.As(err, &httpErr) && httpErr.Status == 403 {
		// disable streaming path; use inline-card JSON permanently
	}
}

Prevention

When it happens

Trigger: POST /open-apis/cardkit/v1/cards returns 4xx/5xx: invalid or expired tenant_access_token (401), missing cardkit card scope (403), card JSON too large (413), Feishu server error (5xx), or a proxy returning 502/504. Also fires when apiResp is nil despite a nil error from the client.

Common situations: App not granted Cardkit (Card Entity) permission in the Feishu developer console; token refresh raced and an expired token was used; very large streaming card JSON exceeding body limits; corporate proxy intercepting the call.

Related errors


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