sipeed/picoclaw · error
feishu edit: card build failed: %w
Error message
feishu edit: card build failed: %w
What it means
FeishuChannel.EditMessage first converts the new content into a Feishu interactive-card JSON payload via buildMarkdownCard; this error means that conversion failed before any API request was made. The underlying build error is wrapped, so the cause (typically JSON marshaling or card-schema limits) is visible in the message. Editing cannot fall back to plain text the way Send does, because Message.Patch only accepts card content.
Source
Thrown at pkg/channels/feishu/feishu_64.go:249
} else if hasTrackedMsg {
c.dismissTrackedToolFeedbackMessage(ctx, msg.ChatID, trackedMsgID)
}
return []string{msgID}, nil
}
// If text also fails, return the text error
return nil, textErr
}
// For other errors, return the original card error
return nil, err
}
// EditMessage implements channels.MessageEditor.
// Uses Message.Patch to update an interactive card message.
func (c *FeishuChannel) EditMessage(ctx context.Context, chatID, messageID, content string) error {
cardContent, err := buildMarkdownCard(content)
if err != nil {
return fmt.Errorf("feishu edit: card build failed: %w", err)
}
req := larkim.NewPatchMessageReqBuilder().
MessageId(messageID).
Body(larkim.NewPatchMessageReqBodyBuilder().Content(cardContent).Build()).
Build()
resp, err := c.client.Im.V1.Message.Patch(ctx, req)
if err != nil {
return fmt.Errorf("feishu edit: %w", err)
}
if !resp.Success() {
c.invalidateTokenOnAuthError(resp.Code)
return fmt.Errorf("feishu edit api error (code=%d msg=%s)", resp.Code, resp.Msg)
}
return nil
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Print the wrapped cause (%v) - it names the exact build failure (marshal error vs schema limit)
- Simplify the markdown before editing: split oversized tables, trim the content, or strip unsupported syntax
- If edits keep failing, fall back to delete + re-send as a plain text message instead of patching the card
- Unit-test buildMarkdownCard against your real agent output shapes
Example fix
// before
if err := ch.EditMessage(ctx, chatID, msgID, hugeMarkdown); err != nil {
return err // stuck: card cannot be patched
}
// after
if err := ch.EditMessage(ctx, chatID, msgID, hugeMarkdown); err != nil {
if strings.Contains(err.Error(), "card build failed") {
_ = ch.DeleteMessage(ctx, chatID, msgID)
_, err2 := ch.Send(ctx, bus.OutboundMessage{ChatID: chatID, Content: plainText(hugeMarkdown)})
return err2
}
return err
} Defensive patterns
Strategy: fallback
Validate before calling
// dry-run the card build before editing, with the same builder
if _, err := feishu.BuildMarkdownCard(newContent); err != nil {
// will fail in EditMessage too: use the plain-text path now
} Type guard
func isCardBuildFailure(err error) bool {
return err != nil && strings.Contains(err.Error(), "card build failed")
} Try / catch
err := ch.EditMessage(ctx, chatID, msgID, content)
if err != nil {
if isCardBuildFailure(err) {
// cards cannot be patched as text: replace the message instead
_ = ch.DeleteMessage(ctx, chatID, msgID)
_, err = ch.Send(ctx, bus.OutboundMessage{ChatID: chatID, Content: content})
}
}
return err Prevention
- Bound markdown size (tables, rows) before it reaches EditMessage
- Keep a regression corpus of real agent outputs and run buildMarkdownCard over it in CI
- Treat edit failures as 'resend' triggers, not dead ends
- Never blind-retry a card build failure - the content deterministically fails again
When it happens
Trigger: EditMessage is called with markdown the card builder cannot serialize: malformed nesting, unsupported constructs, or content exceeding Feishu card constraints (e.g. too many table columns/rows), causing buildMarkdownCard to return an error.
Common situations: Editing a long streaming/agent reply whose final markdown grew past card limits; markdown produced by a tool that emits exotic syntax; concurrent edit of a huge code block or table.
Related errors
- feishu placeholder: card build failed: %w
- feishu edit: %w
- feishu edit api error (code=%d msg=%s)
- feishu send card: %w
- feishu channel is not supported on 32-bit architectures (arm
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/0bf81785aae47111.
Report an issue: GitHub.