fix(slack): guard rich_text builders against empty content rejected as invalid_blocks - #59621
Conversation
Related: #56618 / #57128 (both fix null |
|
Thanks for the focused guard. The reported premise is still present on current main: The proposed guard covers every direct Automated hermes-sweeper review. |
…s invalid_blocks
Slack rejects a rich_text_section / rich_text_preformatted / rich_text_quote
whose elements list is empty or contains a zero-length text element, and a
header whose plain_text is empty. The Block Kit renderer emits exactly those
shapes for common inputs: a markdown table with a blank cell or a ragged
(short) row, an empty fenced code block, a blank quote line, an empty list
item, and an emphasis-only header ("# ***"). Any one of them poisons the
whole payload — chat.postMessage fails with invalid_blocks ("missing element"
/ "must be more than 0 characters") and the message loses its rich rendering
entirely.
Route every rich_text builder's child elements through _nonempty_elements
(drop zero-length text elements; substitute a single space when nothing
remains) and skip headers that reduce to empty after markdown-marker
stripping. Observed in production via live chat.postMessage rejections;
regression tests cover each case plus a well-formed-content control.
Complementary to NousResearch#56618 (column_settings hardening + no-blocks retry): that
change makes Block Kit rejections recoverable, this one makes the common
empty-content cases render correctly in the first place. No overlapping hunks.
f9ce7c1 to
22398f8
Compare
|
Merged via #69317 — your commit was cherry-picked onto current main with your authorship preserved in git history: your empty rich_text guards were cherry-picked (the AUTHOR_MAP edit was swapped for a contributors/emails/ mapping — that dict is frozen). Thanks for the contribution! |
What does this PR do?
The Block Kit renderer can emit rich_text shapes that Slack rejects with
invalid_blocks, which makes the whole payload fail — the message loses itsrich rendering entirely. Slack's rules:
rich_text_section/rich_text_preformatted/rich_text_quotemust nothave an empty
elementslist, nor atextelement of zero length(
"missing element"/"must be more than 0 characters"), andheadermust not carry an emptyplain_text.The renderer produces exactly those shapes for common inputs:
""rich_text_preformattedwith a 0-char text element>)rich_text_quoteelements-)# ***,#)headerwith emptyplain_textAny single one of these poisons the entire
chat.postMessagepayload. All ofthem were hit in production (live
invalid_blocksrejections; the empty tablecell being the most frequent — ragged rows are everywhere in LLM output).
Fix: route every rich_text builder's child elements through a shared
_nonempty_elementsguard — drop zero-length text elements, substitute asingle space when nothing remains (renders as blank, stays schema-valid) — and
skip headers that reduce to empty after markdown-marker stripping.
Relationship to #56618 (harden rich table block fallback): complementary,
no overlapping hunks. #56618 fixes
column_settingsand adds a retry-without-blocks safety net, i.e. it makes Block Kit rejections recoverable; this PR
makes the common empty-content cases render correctly in the first place —
without it, any table containing a single empty cell silently loses native
rendering on every send even with the retry net in place. This PR deliberately
does not touch
_table_block/column_settingsor the adapter send path soboth PRs apply cleanly in either order.
Follows #58691 (config bridge that makes the
rich_blocksopt-in reachable) —these rendering failures surface as soon as the flag is actually enabled.
Related Issue
The empty-content rejection has no dedicated issue (root cause and repro are
above). The neighbouring
column_settingsfailure is tracked in #56615 andfixed by #56618.
Type of Change
Changes Made
plugins/platforms/slack/block_kit.py— new_nonempty_elementshelper;applied in
_rich_text_cell,_preformatted_block,_quote_block,_list_block;_header_blockreturnsNonefor empty headers and therender_blockscaller skips it.tests/gateway/test_slack_block_kit.py— newTestEmptyContentGuards:ragged/blank table cells, empty code fence + blank quote + empty list item,
multi-line quote keeps its
"\n"separators (guard must not strip them),emphasis-only header dropped, and a well-formed-content control.
scripts/release.py— AUTHOR_MAP entry for the commit email (also added infix(slack): bridge rich_blocks opt-in from a top-level slack: block to extra #58691; whichever lands second rebases the one line away).
How to Test
pytest tests/gateway/test_slack_block_kit.py tests/gateway/test_slack_block_kit_adapter.py -qheader reach the payload) and pass with the guard.
rich_blocksenabled: a tablecontaining blank and ragged cells now posts as a native
tableblock; thesame message previously failed with
invalid_blocks: must be more than 0 characters [json-pointer:/blocks/N/rows/…].Checklist
Code
fix(slack): …)test_slack_block_kit.py+test_slack_block_kit_adapter.py: all passDocumentation & Housekeeping
_nonempty_elements,_rich_text_cell,_header_block)cli-config.yaml.example— N/A (no config change)CONTRIBUTING.md/AGENTS.md— N/A