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, nilView on GitHub (pinned to 4000b2338a)
Solutions
- Read the logged status code: 401/403 → fix app credentials or grant cardkit:card permission in the Feishu developer console; 5xx → retry later
- Confirm the app version containing the Cardkit permission is published/released (permissions must be released, not just enabled)
- Reduce the card JSON size (trim schema/whitespace) if 413
- 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
- Grant and RELEASE cardkit permissions in the Feishu developer console
- Keep tokens fresh — the code already retries with a fresh tenant token
- Keep card JSON small to avoid 413
- Alert on sustained 5xx from Feishu endpoints
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
- %s: stream rich card text: HTTP status %d
- too many redirects
- init failed: %w
- begin failed: %w
- poll failed: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/47da768cba0ba985.
Report an issue: GitHub.