Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Slack By James Joyner IV · · 9 min read Last reviewed Jul 2026

Slack Error Guide: 'invalid_blocks' — Fix Malformed Block Kit Payloads

Quick answer

Fix the Slack invalid_blocks error: validate Block Kit structure, catch bad block/element types, over-length text, too many blocks, and missing fields, with real curl and response_metadata examples.

  • #slack
  • #api
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this Slack error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

The invalid_blocks error means Slack could not parse or accept the blocks array you sent to a method like chat.postMessage, chat.update, views.open, or views.publish. The payload was syntactically valid JSON, but the Block Kit structure inside it violated a rule: an unknown block or element type, a field that is too long, too many blocks, a missing required property, or a value that is not allowed in that position. Unlike a rate limit, this is a payload problem — retrying the identical body will fail every time until you fix the blocks.

The response returns HTTP 200 with ok: false, and the useful detail is in response_metadata.messages:

{
    "ok": false,
    "error": "invalid_blocks",
    "response_metadata": {
        "messages": [
            "invalid block: text field cannot exceed 3000 characters [json-pointer:/blocks/2/text/text]"
        ]
    }
}

The json-pointer in the message tells you exactly which block and field failed — always read it before touching anything else.

Symptoms

  • Method call returns 200 with "ok": false and "error": "invalid_blocks".
  • response_metadata.messages contains one or more human-readable reasons with json-pointer locations.
  • Messages that render in Block Kit Builder still fail from your code (usually a length or count difference from real data).
  • A message that worked with test data fails once real content (long logs, many rows) is substituted.
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","blocks":[{"type":"section"}]}'
{
    "ok": false,
    "error": "invalid_blocks",
    "response_metadata": { "messages": ["invalid block: missing required field [json-pointer:/blocks/0]"] }
}

Common Root Causes

1. Section block with no text and no fields

A section must contain either text or fields. An empty section is rejected.

curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","blocks":[{"type":"section"}]}'
{"ok":false,"error":"invalid_blocks","response_metadata":{"messages":["invalid block: missing required field [json-pointer:/blocks/0]"]}}

2. Text field exceeds the length limit

A section text is capped at 3000 characters; a plain-text button text at 75. Dumping a raw log line into a section blows past 3000.

BIG=$(head -c 4000 < /dev/zero | tr '\0' 'x')
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channel\":\"C0123456789\",\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"$BIG\"}}]}"
{"ok":false,"error":"invalid_blocks","response_metadata":{"messages":["invalid block: text field cannot exceed 3000 characters [json-pointer:/blocks/0/text/text]"]}}

3. Too many blocks

A message allows at most 50 blocks; a modal/home view allows 100. Rendering one block per table row overflows quickly.

BLOCKS=$(python3 -c 'import json;print(json.dumps([{"type":"divider"} for _ in range(60)]))')
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channel\":\"C0123456789\",\"blocks\":$BLOCKS}"
{"ok":false,"error":"invalid_blocks","response_metadata":{"messages":["too many blocks (max 50) [json-pointer:/blocks]"]}}

4. Unknown or misspelled block/element type

A typo in a type ("secton", "buttton") or using a modal-only element in a message yields invalid_blocks.

curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","blocks":[{"type":"secton","text":{"type":"mrkdwn","text":"hi"}}]}'
{"ok":false,"error":"invalid_blocks","response_metadata":{"messages":["unknown block type \"secton\" [json-pointer:/blocks/0/type]"]}}

5. Text object type mismatch

Some fields require plain_text (button labels, input labels, option text) and reject mrkdwn. Sending the wrong type fails validation.

curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","blocks":[{"type":"actions","elements":[{"type":"button","text":{"type":"mrkdwn","text":"*Go*"}}]}]}'
{"ok":false,"error":"invalid_blocks","response_metadata":{"messages":["invalid block: button text must be plain_text [json-pointer:/blocks/0/elements/0/text]"]}}

6. Input block used outside a modal

input blocks are only valid in views (modals/home tab), not in messages. Putting one in chat.postMessage fails.

curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","blocks":[{"type":"input","label":{"type":"plain_text","text":"Name"},"element":{"type":"plain_text_input","action_id":"n"}}]}'
{"ok":false,"error":"invalid_blocks","response_metadata":{"messages":["input blocks are not allowed in messages [json-pointer:/blocks/0]"]}}

Diagnostic Workflow

Step 1: Read response_metadata.messages and the json-pointer

The pointer is the fastest path to the offending field. Print just the messages array:

curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d @payload.json | python3 -c 'import sys,json;print("\n".join(json.load(sys.stdin).get("response_metadata",{}).get("messages",[])))'
invalid block: text field cannot exceed 3000 characters [json-pointer:/blocks/2/text/text]

/blocks/2/text/text means the third block’s text is too long.

Step 2: Confirm the JSON is well-formed first

invalid_blocks is a semantic error, not a JSON parse error — but validate the file locally so you are not chasing a stray comma:

python3 -m json.tool payload.json > /dev/null && echo "valid JSON"

Step 3: Isolate the failing block

Post the blocks one at a time (or bisect the array) to find which index Slack rejects when the pointer is ambiguous:

python3 -c 'import json;b=json.load(open("payload.json"))["blocks"];print(json.dumps(b[2:3]))' > one.json
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channel\":\"C0123456789\",\"blocks\":$(cat one.json)}"

Step 4: Check counts and lengths against the limits

python3 - <<'PY'
import json
b=json.load(open("payload.json"))["blocks"]
print("block count:", len(b), "(max 50 msg / 100 view)")
for i,blk in enumerate(b):
    t=blk.get("text",{}).get("text","")
    if len(t)>3000: print(f"block {i} section text {len(t)} > 3000")
PY

Step 5: Validate the fixed payload with views.open (dry run for modals)

For modal blocks, views.open surfaces the same invalid_blocks detail without posting to a channel:

curl -s -X POST https://slack.com/api/views.open \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"trigger_id":"REDACTED_TRIGGER_ID","view":{"type":"modal","title":{"type":"plain_text","text":"Test"},"blocks":[]}}'

Example Root Cause Analysis

A deploy bot posts a summary block per changed service. On a big release it sends 63 section blocks and starts failing:

curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d @deploy_summary.json | python3 -c 'import sys,json;d=json.load(sys.stdin);print(d["error"], d.get("response_metadata",{}).get("messages"))'
invalid_blocks ['too many blocks (max 50) [json-pointer:/blocks]']

Test releases only touched a handful of services, so the 50-block ceiling was never hit locally. The fix is to cap the message and move the long list into a thread or a file: render a summary header plus the top N services in the main message, then attach the full list as a snippet or post the remainder in the thread.

python3 -c 'import json;b=json.load(open("deploy_summary.json"))["blocks"];print("kept", min(len(b),48))'

Capping at 48 blocks (leaving headroom under 50) and threading the overflow makes the post succeed while keeping the channel readable.

Prevention Best Practices

  • Always read response_metadata.messages and the json-pointer; it names the exact block and field, so never debug invalid_blocks blind.
  • Enforce Block Kit limits in code before sending: ≤50 blocks per message (≤100 per view), 3000-char section text, 75-char button/label text.
  • Test with realistic, worst-case data (long log lines, many rows), not small fixtures — most invalid_blocks failures only appear at real volume.
  • Use plain_text where required (buttons, input labels, option text) and mrkdwn only where allowed.
  • Keep input blocks in modals/home views; use section + accessory or actions in messages.
  • Prototype in Block Kit Builder, then diff its JSON against what your code emits with real content.
  • For ad-hoc triage of a failing payload, the free incident assistant can point at the offending field. See more in Slack guides.

Quick Command Reference

# Print only the validation messages
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d @payload.json | python3 -c 'import sys,json;print("\n".join(json.load(sys.stdin).get("response_metadata",{}).get("messages",[])))'

# Validate JSON locally
python3 -m json.tool payload.json > /dev/null && echo ok

# Count blocks and flag over-length section text
python3 - <<'PY'
import json
b=json.load(open("payload.json"))["blocks"]
print("blocks:",len(b))
for i,blk in enumerate(b):
    t=blk.get("text",{}).get("text","")
    if len(t)>3000: print("over-length section at",i,len(t))
PY

# Post a single block to isolate the failure
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","blocks":[ONE_BLOCK]}'

Conclusion

invalid_blocks is a structure problem in your Block Kit payload, not a transient failure — the same body will always be rejected until you fix it. The usual root causes:

  1. A section with neither text nor fields.
  2. Text past a length limit (3000 for sections, 75 for buttons/labels).
  3. More than 50 blocks in a message (100 in a view).
  4. An unknown or misspelled block/element type.
  5. A plain_text vs mrkdwn mismatch.
  6. An input block used in a message instead of a modal.

Read response_metadata.messages, jump to the json-pointer it gives you, enforce Block Kit limits in code, and test with real-sized data — that turns invalid_blocks from a mystery into a one-line fix.

Free download · 368-page PDF

Fixed it? Get 500 Slack & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.