feishu: replace deprecated card V2 action container with column_set#79832
feishu: replace deprecated card V2 action container with column_set#79832xiabee wants to merge 2 commits into
action container with column_set#79832Conversation
Feishu's V2 card schema no longer accepts the V1 `{ tag: "action", actions:
[...] }` wrapper. The Open Platform API now rejects every button-bearing card
with `feishu_code 230099 / sub-code 200861 / "cards of schema V2 no longer
support this capability; ErrorValue: unsupported tag action"`. As a result no
exec/plugin approval card, `/quick`-actions card, or
`buildFeishuCardElementForBlock("buttons")` block can be delivered: the agent
sits forever waiting on an approval the user never sees.
Add a small `actionRowToColumnSet(buttons)` helper in `outbound.ts` that
emits the V2-supported shape — a `column_set` with one auto-width column per
button — and route the four production sites through it:
- extensions/feishu/src/outbound.ts (sanitizeNativeFeishuCardElement,
buildFeishuCardElementForBlock)
- extensions/feishu/src/card-ux-approval.ts (exec approval card)
- extensions/feishu/src/card-ux-launcher.ts (quick actions card)
`sanitizeNativeFeishuCardElement` keeps accepting legacy V1 input (`tag:
"action"`) for back-compat with callers that still hand-build native cards in
the old format, and now also accepts forward-compat input (`tag:
"column_set"`). It always emits the V2 shape.
Tests adjusted to assert the new shape; the legacy-V1 input branch is still
covered via the `normalizes caller-supplied native Feishu cards` test which
sends `tag: "action"` in and expects `tag: "column_set"` out.
Refs openclaw#79824
|
Codex review: needs changes before merge. Reviewed July 3, 2026, 8:47 PM ET / July 4, 2026, 00:47 UTC. Summary PR surface: Source +39, Tests +25. Total +64 across 6 files. Reproducibility: yes. Current main and v2026.6.11 still build Feishu approval and quick-action rows with Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof path suggestion Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Security Review findings
Review detailsBest possible solution: Refresh or replace this branch on current main, keep the fix inside the Feishu plugin card-building boundary, preserve current presentation-card behavior, and validate focused Feishu card tests plus real delivery proof if the final row layout changes. Do we have a high-confidence way to reproduce the issue? Yes. Current main and v2026.6.11 still build Feishu approval and quick-action rows with Is this the best way to solve the issue? Yes in direction, but not as submitted. The Feishu plugin card builders and sanitizer are the right boundary, but the branch predates current main’s presentation-card renderer and must be refreshed before merge. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f59310a555d7. Label changesLabel justifications:
Evidence reviewedPR surface: Source +39, Tests +25. Total +64 across 6 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (1 earlier review cycle)
|
|
This pull request has been automatically marked as stale due to inactivity. |
Summary
Fixes #79824.
Feishu's V2 card schema dropped the V1
{ tag: "action", actions: [...] }container. The Open Platform API now rejects every button-bearing card with:
End-user impact: no Feishu approval card,
/quick-actions card, or buttonsblock reaches the chat at all — the agent silently hangs waiting on
approvals the operator never sees, and
gateway/delivery-recoverykeepsre-firing the failing send.
This is distinct from the previously-fixed
230099reports (#53310, #53771)which were about
ErrCode 200621 / parse card json errin markdown cards.The new sub-code is
200861 / unsupported tag action, surfaced after Feishutightened V2 schema validation.
Change
Add a small
actionRowToColumnSet(buttons)helper inoutbound.tsthatemits the V2-supported shape — a
column_setwith one auto-width column perbutton — and route the four production sites through it:
extensions/feishu/src/outbound.tssanitizeNativeFeishuCardElementextensions/feishu/src/outbound.tsbuildFeishuCardElementForBlock("buttons")extensions/feishu/src/card-ux-approval.tsextensions/feishu/src/card-ux-launcher.ts/quickactions cardsanitizeNativeFeishuCardElement:{ tag: "action", actions: [...] }for back-compat with callers that hand-build native cards.
{ tag: "column_set", columns: [...] },flattening single-button columns through the existing button sanitizer.
The emitted
column_setusesflex_mode: "none",horizontal_spacing: "default",and one column per button with
width: "auto"+vertical_align: "center",giving the same horizontal-row layout V1
actionproduced.Real behavior proof
Behavior addressed: Feishu Open Platform rejects every exec-approval and
/quick-actions card built by@openclaw/feishubecause the cards include atop-level
{ tag: "action", actions: [...] }element that V2 schema dropped.The card never reaches Feishu, so approval flows silently hang and
gateway/delivery-recoveryfloods retries.Real environment tested: OpenClaw
2026.5.7(eeef486) gateway runningon Kylin Linux V10 SP1 (Node v24.15.0),
@openclaw/[email protected]installedvia
openclaw plugins install, Feishu Lark Win desktop client (May 2026build) connected to a real Feishu tenant. Patch was applied as a hot-rewrite
on the production
dist/send-DowxxbpH.js(recursive walk wrapping everyJSON.stringify(card)to convert{tag:"action", actions:[...]}into{tag:"column_set", columns:[...]}) — semantically identical to the sourceedits in this PR. Hot-patch was syntax-checked with
node --checkbeforerestarting the gateway via
systemctl --user restart openclaw-gateway.Exact steps or command run after this patch:
cp dist/send-DowxxbpH.js dist/send-DowxxbpH.js.bak__ocMigrateAction(card)recursive rewriter and replace bothJSON.stringify(card)call sites indist/send-DowxxbpH.js.node --check dist/send-DowxxbpH.js→syntax OK.rm /tmp/jiti/dist-send-*.cjsto invalidate any jiti compile cache.systemctl --user daemon-reload && systemctl --user restart openclaw-gateway.gateway/exec-approvals(mainSecurity: fullAsk,Mode: foreground).After-fix evidence:
Before the fix, every approval delivery failed with the V2 schema rejection
(redacted runtime log excerpt copied from
/tmp/openclaw/openclaw-2026-05-09.log):After applying the rewrite + restarting the gateway, the same
gateway/exec-approvalspath delivers the card cleanly and the user-clickedbutton comes back as a structured card action (live log excerpt, same file):
Searching the post-restart log window for the V2 schema rejection returns
zero hits:
The Feishu-side render of the card after the fix shows three buttons —
Allow Once (default), Allow Always (default), Deny (danger) — laid out
in a single horizontal row, matching the original V1
actionrow visual.gateway/exec-approvalsdeliver400 / feishu_code 230099 / unsupported tag actionfeishu_msg_idreturnedhandling structured card action feishu.payload.button→ agent receives/approve <id> <decision>Observed result after fix: Feishu approval cards render with working
Allow Once / Allow Always / Deny buttons in a column_set row; clicking a
button surfaces a
feishu.payload.buttonevent inchannels/feishuand the matching/approve <id> <decision>text isdispatched to the agent.
gateway/delivery-recoveryno longer retries thecard send. No more
feishu_code 230099/200861 unsupported tag actionentries appear in the gateway log after restart.
What was not tested:
pnpm test extensions/feishuandpnpm tsgo*— nopnpmtoolchain on thedeveloper host where edits were authored; relying on CI for Vitest/tsgo
coverage. Test assertions for the four mutated production sites and the
shared
expectSentCardHasP2pActionhelper were updated in lockstep withthe source edits.
column_setinput branch insanitizeNativeFeishuCardElementisexercised only via the legacy
tag: "action"back-compat path in theexisting
normalizes caller-supplied native Feishu cardstest; a dedicatedforward-compat case for
tag: "column_set"input is not yet added. Happyto add one if reviewers want it.
Side note (separate issue worth filing?)
While debugging I also noticed that
pending approvals are cleared after expiry or restart(per theAPPROVAL_NOT_FOUND_DETAILS.remediationstringin
approval-shared). For long-running agents that block on approvals thismeans 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 pendingapprovals so they survive restarts would meaningfully improve the UX.
Happy to file that as a separate issue and possibly a follow-up PR if
maintainers agree it's in scope.