Slack Block Kit checker
Paste a Block Kit payload and see the answer Slack gives when you send it: the exact invalid_blocks lines, the block that breaks, and a fixed payload that passes. Every limit was measured in a real workspace.
Paste a Block Kit Builder payload, a blocks list, a webhook body or a chat.postMessage body.
What Slack answers
Slack rejects this: invalid_blocks
HTTP 400, body: invalid_blocks
The webhook sends only invalid_blocks. chat.postMessage would name the problem:
[ERROR] failed to match all allowed schemas [json-pointer:/blocks/0/text] [ERROR] must be less than 151 characters [json-pointer:/blocks/0/text/text] [ERROR] no more than 10 items allowed [json-pointer:/blocks/2/fields]
Slack stops at schema errors: 1 more problem shows after you fix these, marked “next” below.
Top-level text, 26 characters as Slack stores it: shown as the notification and fallback, not split.
The message, block by block
Bars show how close each field is to the limit Slack enforced in our tests. Red rows are the ones in Slack’s answer.
- 0headerDeploy finished: api 3.2.0 to production in us-east-1, eu-west-1 and ap-northeast-2, 412 of 412 tests passed, 3 migrations applied, no rollbacks neededHeader text151 / 150
- [ERROR] failed to match all allowed schemas [json-pointer:/blocks/0/text]
- [ERROR] must be less than 151 characters [json-pointer:/blocks/0/text/text]
- 1section*api 3.2.0* is live. Started by <@U0123ABCD> at 14:02 UTC, took 6 min 41 s.
- 2section12 fieldsFields12 / 10
- [ERROR] no more than 10 items allowed [json-pointer:/blocks/2/fields]
- 3actionsOpen logs · Roll backElements2 / 25
- next: [ERROR] `action_id` "open" already exists [json-pointer:/blocks/3/elements/1/action_id]
- 4contextPipeline #8812 · main @ 4f2c9e1Elements1 / 10
Fixed payload
Passes every check on this page for webhook- Block 0 header: cut from 151 to 150 characters.
- Block 2: 12 fields split into 2 sections of up to 10.
- action_id "open" renamed to "open-2" (two elements in one actions block had it).
{
"text": "Deploy finished: api 3.2.0",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Deploy finished: api 3.2.0 to production in us-east-1, eu-west-1 and ap-northeast-2, 412 of 412 tests passed, 3 migrations applied, no rollbacks need…"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*api 3.2.0* is live. Started by <@U0123ABCD> at 14:02 UTC, took 6 min 41 s."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*api*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*web*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*worker*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*billing*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*search*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*auth*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*mailer*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*cron*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*exports*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*imports*\nhealthy"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*media*\nhealthy"
},
{
"type": "mrkdwn",
"text": "*admin*\nhealthy"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Open logs"
},
"action_id": "open",
"url": "https://example.com/logs"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Roll back"
},
"action_id": "open-2",
"style": "danger"
}
]
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Pipeline #8812 · main @ 4f2c9e1"
}
]
}
]
}Rough preview of message 1
How to check a Block Kit message
- Paste the JSON: a Block Kit Builder payload, a blocks list, a webhook body or a chat.postMessage body. A JSON syntax error shows its line and column.
- Pick how you send it: incoming webhook, chat.postMessage or chat.update. Each one answers differently, and long text is cut differently.
- Read Slack's answer. A webhook only says invalid_blocks, so the page also shows the lines chat.postMessage would return, and marks the block in the list below.
- Copy the fixed payload or the curl command. Every change is listed; the page checks the fixed payload again before it shows it.
Frequently asked questions
What does invalid_blocks mean in Slack?
Slack refused your blocks and posted nothing. chat.postMessage and chat.update list the reasons in response_metadata.messages, one line each, like "[ERROR] must be less than 151 characters [json-pointer:/blocks/0/text/text]". The json-pointer is the path to the bad field, counting blocks from 0. An incoming webhook answers HTTP 400 with only the word invalid_blocks.
What are the Block Kit limits?
What we measured on 1 October 2026, last value that worked: 50 blocks per message, 3,000 characters of section text, 150 for a header, 75 for a button label, 25 elements in an actions block, 10 in a context block, 10 fields of up to 2,000 characters each, 2,000 for a button value, 3,000 for a button url, 255 for action_id and block_id, 100 select options of up to 75 characters, and 12,000 characters per markdown block. Fifty full sections, 150,000 characters, posted in one message.
Why does Slack show only one error when my message has several?
Slack checks in stages and stops at the first stage that fails. With more than 50 blocks you get only the block count. Then it checks lengths and types in every block. Only when those pass does it report a duplicate block_id, a duplicate action_id inside one block, a section with neither text nor fields, or an image it cannot download. The page shows the later ones as "next".
Can I check blocks with blocks.validate instead?
It needs a token, and it did not match chat.postMessage in our tests. Out of 73 payloads it passed 5 that chat.postMessage refused (duplicate block_id, duplicate action_id, a section with no text or fields, an image URL that returned 404, an empty message) and refused one that chat.postMessage posted: 50 sections of 3,000 characters. This page follows chat.postMessage and the webhook.
What happens to long text in a Slack message?
It depends on how you send it. chat.postMessage without blocks cuts the text into 4,000-character messages and returns the ts of the last one only; a last piece of 40 characters or fewer stays on the message before it. An incoming webhook keeps the first 16,000 characters and drops the rest while answering ok. chat.update refuses anything over 4,000 with msg_too_long. With blocks in the message, 5,000 characters of text stayed one message. Slack counts the text as it stores it: & is &, an emoji is its :name:, and chat.update counts bytes, so é counts 2 and 가 counts 3.
Related reading
Slack Green does one thing: it keeps your Slack presence dot green while you are actually working, so a quiet keyboard does not mark you away. Try it free.