Skip to content

Expose cumulative block character limit as a constant and include count in msg_blocks_too_long error response #2867

@frankie303

Description

@frankie303

chat.postMessage rejects blocks with msg_blocks_too_long but the cumulative limit is undocumented, not validated client-side, and the error response contains no detail about what exceeded the limit or by how much. We'd love some clarity on how the limit is calculated so we can avoid it proactively.

Context

We built a CI analysis bot that posts rich_text + section + header + divider blocks via chat.postMessage (mention path). The slash command path uses respond() and works fine with identical blocks — the limit only affects chat.postMessage.

In #2509, @vegeris mentioned "12,000 text characters with a buffer for block containers." We implemented measureTextLength() that walks the block tree and sums all .text string properties. It reported 11,341 — under 12,000 — yet Slack still rejected with msg_blocks_too_long. We also note that we don't use markdown blocks (type: 'markdown'). The documented 12,000 cumulative limit on the markdown block docs applies to a block type we don't use, and there is no documented cumulative limit for rich_text, section, header, or divider blocks.

Production measurements

We instrumented every chat.postMessage call with three measurements:

  • blocks text — walks the blocks JSON tree and sums all .text string values, including nested text objects (e.g., HeaderBlock.text.text, SectionBlock.text.text, rich_text element .text)
  • blocks JSONJSON.stringify(blocks).length
  • text param — length of the text argument passed to chat.postMessage
blocks text blocks JSON text param blockCount Result
4,649 7,768 2,497 28 Accepted
6,020 9,542 3,265 33 Accepted
9,597 15,561 6,463 50 Accepted
11,341 17,694 10,400 45 Rejected

We tested several ways of measuring the payload size — summing text content across blocks, JSON-serialized block size, the text parameter length, and combinations of these — but none produced a consistent threshold matching the ~13,200 figure from #2509. The tightest bound we found is blocks JSON between 15,561 (accepted) and 17,694 (rejected).

Error response

The response doesn't help us debug:

{
  "ok": false,
  "error": "msg_blocks_too_long",
  "response_metadata": {
    "scopes": ["chat:write", "..."],
    "acceptedScopes": ["chat:write"]
  }
}

The response doesn't include a character count, limit value, or indication of what was measured — which makes it difficult to debug without trial and error.

What would help

Any of these would help developers handle this proactively:

  1. Document the cumulative limit for non-markdown block types — the current 12,000 limit is documented only for markdown blocks. What is the limit for rich_text, section, header, and mixed-type payloads?
  2. Clarify what is counted — text content only? serialized JSON? structural overhead? Does the text parameter count toward the limit?
  3. Include the count and limit in the error response — e.g., "detail": "blocks content is 17,694 characters, limit is 16,000" — this alone would eliminate all guesswork
  4. Add client-side validation in @slack/web-api that checks before sending and throws a descriptive error (related: Test utility for validating blocks #1652 has requested a block validation utility)
  5. Export the limit as a runtime constant from @slack/web-api so developers can validate blocks before sending — per-block limits already exist in @slack/types JSDoc but the cumulative limit is absent entirely

Current workaround

We plan to catch msg_blocks_too_long, reduce the content, rebuild blocks, and retry. This should work but results in extra API calls that could be avoided with a documented limit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions