Summary
The bundled @openclaw/feishu plugin (verified on 2026.5.7) emits Feishu cards that wrap action buttons in a top-level { tag: "action", actions: [...] } container. Feishu's V2 card schema has dropped this container, so every button-bearing card is rejected by https://open.feishu.cn/open-apis/im/v1/messages with:
http_status: 400
feishu_code: 230099
feishu_msg : Failed to create card content, ext=ErrCode: 200861;
ErrPath : ROOT -> body -> elements -> [1](tag: action);
ErrMsg : cards of schema V2 no longer support this capability;
ErrorValue: unsupported tag action
Because the cards never reach Feishu, no exec or plugin approval can be delivered to a Feishu approver — every approval is silently lost on the chat side. The gateway then keeps retrying via gateway/delivery-recovery, flooding error logs.
This is distinct from the previously-fixed code 230099 bugs (#53310, #53771) which were about ErrCode 200621 / parse card json err in markdown cards. The new sub-error is ErrCode 200861 / unsupported tag action, surfaced after Feishu tightened V2 schema validation.
Repro
- OpenClaw gateway 2026.5.7 with
@openclaw/feishu 2026.5.7 enabled, Feishu DM channel configured.
- Run anything that triggers an exec approval, e.g.
git status under mainSecurity: fullAsk.
- Approval card fails to send;
gateway/exec-approvals logs the 230099 error above.
The same code path is used for /quick-actions cards and any buildFeishuCardElementForBlock("buttons") block — they all fail.
Root cause
In @openclaw/feishu (visible in dist/channel.runtime-*.js and dist/monitor.account-*.js, four sites total), button rows are constructed as
{ tag: "action", actions: [ /* button objects */ ] }
Feishu V2 requires multi-button rows to be expressed via column_set (or as top-level button body elements). The action tag was a V1 grouping element and is no longer accepted.
Suggested fix
Replace the action container with a column_set, one column per button:
{
tag: "column_set",
flex_mode: "none",
horizontal_spacing: "default",
columns: actions.map(btn => ({
tag: "column",
width: "auto",
vertical_align: "center",
elements: [btn],
})),
}
I'm running this as a local hot-patch — a recursive rewriter wrapped around every JSON.stringify(card) in dist/send-*.js:
function __ocMigrateAction(node) {
if (!node || typeof node !== 'object') return node;
if (Array.isArray(node)) return node.map(__ocMigrateAction);
if (node.tag === 'action' && Array.isArray(node.actions)) {
return {
tag: 'column_set',
flex_mode: 'none',
horizontal_spacing: 'default',
columns: node.actions.filter(Boolean).map(btn => ({
tag: 'column', width: 'auto', vertical_align: 'center',
elements: [__ocMigrateAction(btn)],
})),
};
}
const out = {};
for (const k in node) if (Object.prototype.hasOwnProperty.call(node, k)) out[k] = __ocMigrateAction(node[k]);
return out;
}
After applying, exec-approval cards render with working Confirm / Cancel / Allow Once / Allow Always / Deny buttons end-to-end on Feishu Lark (Win desktop, May 2026 build).
Happy to send a PR against extensions/feishu (or wherever the source lives in the monorepo) — left it as an issue first since the four call sites would need to flow through the proper sanitizer functions, and the maintainers might prefer a different shape (e.g. a shared cardActionRowToColumnSet helper).
Side note (separate issue worth filing?)
While debugging, I noticed pending approvals are cleared after expiry or restart (per the APPROVAL_NOT_FOUND_DETAILS.remediation string in approval-shared). For long-running agents that block on approvals this means a routine gateway restart silently invalidates every outstanding approval card the user can still see in their Feishu chat — clicking those buttons returns unknown or expired approval id. Persisting pending approvals so they survive restarts would significantly improve UX. I can file that as a separate issue if maintainers agree it's in scope.
Versions
- openclaw:
2026.5.7 (eeef486)
- @openclaw/feishu:
2026.5.7
- node:
24.15.0
- OS: Kylin Linux V10 SP1 (gateway), Win 10 (Feishu desktop client, May 2026 build)
Summary
The bundled
@openclaw/feishuplugin (verified on2026.5.7) emits Feishu cards that wrap action buttons in a top-level{ tag: "action", actions: [...] }container. Feishu's V2 card schema has dropped this container, so every button-bearing card is rejected byhttps://open.feishu.cn/open-apis/im/v1/messageswith:Because the cards never reach Feishu, no exec or plugin approval can be delivered to a Feishu approver — every approval is silently lost on the chat side. The gateway then keeps retrying via
gateway/delivery-recovery, flooding error logs.This is distinct from the previously-fixed
code 230099bugs (#53310, #53771) which were aboutErrCode 200621 / parse card json errin markdown cards. The new sub-error isErrCode 200861 / unsupported tag action, surfaced after Feishu tightened V2 schema validation.Repro
@openclaw/feishu2026.5.7 enabled, Feishu DM channel configured.git statusundermainSecurity: fullAsk.gateway/exec-approvalslogs the230099error above.The same code path is used for
/quick-actions cards and anybuildFeishuCardElementForBlock("buttons")block — they all fail.Root cause
In
@openclaw/feishu(visible indist/channel.runtime-*.jsanddist/monitor.account-*.js, four sites total), button rows are constructed asFeishu V2 requires multi-button rows to be expressed via
column_set(or as top-levelbuttonbody elements). Theactiontag was a V1 grouping element and is no longer accepted.Suggested fix
Replace the
actioncontainer with acolumn_set, one column per button:I'm running this as a local hot-patch — a recursive rewriter wrapped around every
JSON.stringify(card)indist/send-*.js:After applying, exec-approval cards render with working Confirm / Cancel / Allow Once / Allow Always / Deny buttons end-to-end on Feishu Lark (Win desktop, May 2026 build).
Happy to send a PR against
extensions/feishu(or wherever the source lives in the monorepo) — left it as an issue first since the four call sites would need to flow through the proper sanitizer functions, and the maintainers might prefer a different shape (e.g. a sharedcardActionRowToColumnSethelper).Side note (separate issue worth filing?)
While debugging, I noticed
pending approvals are cleared after expiry or restart(per theAPPROVAL_NOT_FOUND_DETAILS.remediationstring inapproval-shared). For long-running agents that block on approvals this means a routine gateway restart silently invalidates every outstanding approval card the user can still see in their Feishu chat — clicking those buttons returnsunknown or expired approval id. Persisting pending approvals so they survive restarts would significantly improve UX. I can file that as a separate issue if maintainers agree it's in scope.Versions
2026.5.7(eeef486)2026.5.724.15.0