Slack Error Guide: 'invalid_payload' — Fix Webhook Bodies
Fix the Slack incoming webhook invalid_payload error: send valid JSON as application/json, escape special characters, and keep Block Kit blocks well-formed.
- #slack
- #api
- #troubleshooting
- #errors
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_payload error is returned by Slack incoming webhooks (the https://hooks.slack.com/services/... URLs) when the body you POST is not something Slack can parse into a message. Unlike Web API methods that return a JSON {ok:false,...}, an incoming webhook responds with a plain-text body and an HTTP 400. The cause is almost always malformed JSON: a trailing comma, an unescaped quote or newline inside text, a wrong Content-Type, sending form data instead of a JSON body, or blocks that don’t match Block Kit’s schema.
The webhook responds with HTTP 400 and this text body:
HTTP/2 400
content-type: text/plain
invalid_payload
It occurs whenever the POST body to an incoming webhook is not valid, well-formed JSON matching Slack’s message shape.
Symptoms
curlto ahooks.slack.comURL returns HTTP400with the literal textinvalid_payload.- Messages with special characters (quotes, backslashes, newlines) fail while plain ones work.
- Sending data as a form field or with the wrong
Content-Type. - Dynamically built JSON breaks when a variable contains a quote or line break.
- A
blocksarray with a malformed or unknown block.
Common Root Causes
1. Malformed JSON
A trailing comma, missing quote, or unbalanced brace makes the body unparseable.
2. Unescaped special characters in text
Raw double quotes, backslashes, or literal newlines inside a string field break the JSON.
3. Wrong Content-Type or form encoding
Posting payload={...} as a form field, or omitting Content-Type: application/json, so Slack doesn’t parse it as a JSON message.
4. Invalid Block Kit structure
A blocks entry missing a required field or using an unknown type.
5. Interpolating untrusted data directly into a JSON string
Building the body with string concatenation, so user/log content with quotes or newlines corrupts it.
Diagnostic Workflow
Step 1: Reproduce the malformed body
# Trailing comma -> invalid JSON
curl -s -o /dev/stderr -w "HTTP %{http_code}\n" -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"text":"deploy done",}'
invalid_payload
HTTP 400
Step 2: Validate the JSON locally before sending
echo '{"text":"deploy done",}' | jq . 2>&1 | head -1
jq: error (at <stdin>:0): Objects must consist of key:value pairs
If jq can’t parse it, neither can Slack. Fix the JSON first.
Step 3: Build the body safely (let a tool do the escaping)
# jq escapes quotes/newlines in $MSG correctly
MSG='He said "deploy is done"
on prod'
BODY=$(jq -n --arg t "$MSG" '{text: $t}')
curl -s -w " HTTP %{http_code}\n" -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$BODY"
ok HTTP 200
A successful incoming webhook returns the literal text ok with HTTP 200.
Step 4: Validate blocks before posting
BODY=$(jq -n '{
blocks: [
{type:"header", text:{type:"plain_text", text:"Deploy complete"}},
{type:"section", text:{type:"mrkdwn", text:"*service:* payments"}}
]
}')
curl -s -w " HTTP %{http_code}\n" -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" -d "$BODY"
ok HTTP 200
Well-formed blocks return ok; a bad block type or missing field returns invalid_payload.
Example Root Cause Analysis
A CI script notifies Slack with the last git commit message in the text. A commit whose message contains a double quote breaks the notification:
COMMIT='fix: handle "null" response from upstream'
curl -s -w " HTTP %{http_code}\n" -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\":\"Deployed: $COMMIT\"}"
invalid_payload HTTP 400
The commit message’s embedded quotes terminate the JSON string early, producing invalid JSON. The fix is to build the body with jq (or the language’s JSON serializer) so special characters are escaped correctly instead of hand-concatenating strings:
BODY=$(jq -n --arg t "Deployed: $COMMIT" '{text: $t}')
curl -s -w " HTTP %{http_code}\n" -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" -d "$BODY"
ok HTTP 200
Letting a JSON serializer handle escaping makes the notification robust to any commit message.
Prevention Best Practices
- Never hand-concatenate JSON; build the body with
jqor your language’s JSON serializer so quotes, backslashes, and newlines are escaped. - Always send
Content-Type: application/jsonwith a raw JSON body — not apayload=form field. - Validate the JSON (and Block Kit structure) locally before sending; treat a
jqparse failure as a blocked send. - Test messages with adversarial content (quotes, emoji, multi-line, backslashes) so escaping is exercised.
- Keep payloads within Slack’s size limits and use Block Kit Builder to validate complex block layouts.
- For ad-hoc triage, the free incident assistant can pinpoint the character that broke a webhook body. See more in Slack guides.
Quick Command Reference
# Validate JSON before sending
echo "$BODY" | jq . >/dev/null && echo "valid" || echo "INVALID JSON"
# Build a safe text payload with jq
BODY=$(jq -n --arg t "$MSG" '{text: $t}')
# Post to an incoming webhook (expect: ok / HTTP 200)
curl -s -w " HTTP %{http_code}\n" -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" -d "$BODY"
Conclusion
invalid_payload from an incoming webhook means the POST body isn’t valid JSON in Slack’s message shape. The usual root causes:
- Malformed JSON (trailing comma, unbalanced braces).
- Unescaped quotes, backslashes, or newlines in
text. - Wrong
Content-Typeor form-encoded body. - Invalid Block Kit structure.
- Concatenating untrusted data straight into the JSON string.
Build the body with a real JSON serializer, send it as application/json, validate it locally first, and a healthy webhook will return ok.
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?
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.