{"record":{"id":"0bf81785aae47111","repo":"sipeed/picoclaw","slug":"feishu-edit-card-build-failed-w","errorCode":null,"errorMessage":"feishu edit: card build failed: %w","messagePattern":"feishu edit: card build failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/feishu/feishu_64.go","lineNumber":249,"sourceCode":"\t\t\t} else if hasTrackedMsg {\n\t\t\t\tc.dismissTrackedToolFeedbackMessage(ctx, msg.ChatID, trackedMsgID)\n\t\t\t}\n\t\t\treturn []string{msgID}, nil\n\t\t}\n\t\t// If text also fails, return the text error\n\t\treturn nil, textErr\n\t}\n\n\t// For other errors, return the original card error\n\treturn nil, err\n}\n\n// EditMessage implements channels.MessageEditor.\n// Uses Message.Patch to update an interactive card message.\nfunc (c *FeishuChannel) EditMessage(ctx context.Context, chatID, messageID, content string) error {\n\tcardContent, err := buildMarkdownCard(content)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"feishu edit: card build failed: %w\", err)\n\t}\n\n\treq := larkim.NewPatchMessageReqBuilder().\n\t\tMessageId(messageID).\n\t\tBody(larkim.NewPatchMessageReqBodyBuilder().Content(cardContent).Build()).\n\t\tBuild()\n\n\tresp, err := c.client.Im.V1.Message.Patch(ctx, req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"feishu edit: %w\", err)\n\t}\n\tif !resp.Success() {\n\t\tc.invalidateTokenOnAuthError(resp.Code)\n\t\treturn fmt.Errorf(\"feishu edit api error (code=%d msg=%s)\", resp.Code, resp.Msg)\n\t}\n\treturn nil\n}\n","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/feishu/feishu_64.go#L231-L267","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nif err := ch.EditMessage(ctx, chatID, msgID, hugeMarkdown); err != nil {\n\treturn err // stuck: card cannot be patched\n}\n\n// after\nif err := ch.EditMessage(ctx, chatID, msgID, hugeMarkdown); err != nil {\n\tif strings.Contains(err.Error(), \"card build failed\") {\n\t\t_ = ch.DeleteMessage(ctx, chatID, msgID)\n\t\t_, err2 := ch.Send(ctx, bus.OutboundMessage{ChatID: chatID, Content: plainText(hugeMarkdown)})\n\t\treturn err2\n\t}\n\treturn err\n}","handlingStrategy":"fallback","validationCode":"// dry-run the card build before editing, with the same builder\nif _, err := feishu.BuildMarkdownCard(newContent); err != nil {\n\t// will fail in EditMessage too: use the plain-text path now\n}","typeGuard":"func isCardBuildFailure(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"card build failed\")\n}","tryCatchPattern":"err := ch.EditMessage(ctx, chatID, msgID, content)\nif err != nil {\n\tif isCardBuildFailure(err) {\n\t\t// cards cannot be patched as text: replace the message instead\n\t\t_ = ch.DeleteMessage(ctx, chatID, msgID)\n\t\t_, err = ch.Send(ctx, bus.OutboundMessage{ChatID: chatID, Content: content})\n\t}\n}\nreturn err","preventionTips":["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"],"tags":["feishu","card","markdown","serialization","edit"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}