Fix Discord Webhook Errors: Every Message and Code
Start with the operation that failed, the HTTP status and Discord's numeric error code. A missing webhook, a missing message and an invalid Components V2 layout need different repairs. The reference below quotes each common message exactly as Discord, discord.js and discord.py print it, so you can match the line in your logs and go straight to the fix.
Every common webhook error, quoted exactly
Discord answers a failed request with an HTTP status and a JSON body such as {"message": "Unknown Webhook", "code": 10015}. The message is the text to search for; the code is the stable number behind it. Find yours in the table, then read the matching section below for the details.
| Message | Code | HTTP | What it means | Fix |
|---|---|---|---|---|
| Invalid Form Body | 50035 | 400 | A field failed validation; the errors object names which one | Read the nested path in errors and fix that field |
| The request body contains invalid JSON. | 50109 | 400 | The body is not valid JSON | Fix quoting or trailing commas; send Content-Type: application/json |
| Cannot send an empty message | 50006 | 400 | Nothing visible was left to post | Add content — or, for Components V2, add ?with_components=true to the URL |
| Unknown Webhook | 10015 | 404 | The webhook was deleted, or the ID in the URL is wrong | Copy the current URL from Integrations → Webhooks |
| Invalid Webhook Token | 50027 | 401 | The token half of the URL is wrong or cut off | Copy the whole URL again; never retype it |
| Unknown Message | 10008 | 404 | Editing a message this webhook did not send, or one that was deleted | Check the message ID, the webhook that sent it and its thread_id |
| Unknown Channel | 10003 | 404 | The thread_id is not under this webhook's channel | Use the parent channel's webhook and a thread from that channel |
| Missing Access | 50001 | 403 | The app cannot see the channel or thread | Give the app access to that channel |
| Missing Permissions | 50013 | 403 | The action needs a permission the caller lacks, such as Manage Webhooks | Grant that permission in the channel, or ask a server admin |
| Maximum number of webhooks reached (15) | 30007 | 400 | A channel holds at most 15 webhooks | Delete unused webhooks in that channel |
| Maximum number of webhooks per guild reached (1000) | 30058 | 400 | The server-wide webhook cap | Remove webhooks the server no longer uses |
| Request entity too large | 40005 | 413 | The upload is over the size limit | Compress, resize or split the attachments |
| You are being rate limited. | — | 429 | Too many requests for this webhook or route | Wait retry_after seconds, then send more slowly |
| Webhooks posted to forum channels must have a thread_name or thread_id | 220001 | 400 | The webhook is in a forum or media channel and the request named no post | See forum posts and threads |
| 405: Method Not Allowed | 0 | 405 | The HTTP method is wrong for that URL | POST to send; PATCH …/messages/ID to edit |
Components V2 errors inside Invalid Form Body
A Components V2 payload that Discord rejects usually comes back as Invalid Form Body with a second, more specific error nested under the field at fault. The top-level message is the same for every validation failure; the nested code is what tells you what to change. This response comes from sending content together with the V2 flag:
| Nested code | Message | Fix |
|---|---|---|
| MESSAGE_CANNOT_USE_LEGACY_FIELDS_WITH_COMPONENTS_V2 | The 'content' field cannot be used when using MessageFlags.IS_COMPONENTS_V2 | Remove content and embeds; put the text in a Text Display component |
| UNION_TYPE_CHOICES | Value of field "type" must be one of (1,). | V2 components were sent without the IS_COMPONENTS_V2 flag — add flags: 32768 |
| BASE_TYPE_REQUIRED | This field is required | A required field is missing at the path shown, such as a Section's accessory |
| BASE_TYPE_MAX_LENGTH | Must be 2000 or fewer in length. | Legacy content is capped at 2,000 characters; shorten it or move the text into Text Displays |
{
"message": "Invalid Form Body",
"code": 50035,
"errors": {
"content": {
"_errors": [
{
"code": "MESSAGE_CANNOT_USE_LEGACY_FIELDS_WITH_COMPONENTS_V2",
"message": "The 'content' field cannot be used when using MessageFlags.IS_COMPONENTS_V2"
}
]
}
}
}The same errors in discord.js and discord.py
Bot libraries wrap the same response in their own exception, so the text in your terminal looks different from the raw JSON while carrying the same two facts. The number in brackets, or after “error code”, is the JSON code from the tables above; the text after the colon is Discord's message.
# discord.js — DiscordAPIError[code]: message
DiscordAPIError[50035]: Invalid Form Body
DiscordAPIError[10015]: Unknown Webhook
DiscordAPIError[50013]: Missing Permissions
# discord.py — status (error code: code): message
discord.errors.HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
discord.errors.NotFound: 404 Not Found (error code: 10015): Unknown Webhook
discord.errors.Forbidden: 403 Forbidden (error code: 50013): Missing PermissionsFind the status, code and failing field
First record whether you were sending a new post, restoring one, updating it or configuring a webhook. In DWEEB, the send error combines the response status with Discord's code and expands nested validation errors into readable paths. Keep those details together when diagnosing the failure.
For example, components[0].components[1].url means the URL on the second child inside the first top-level component. Array positions start at zero. Select that component in the builder and correct that field before changing the rest of the design. The editor's own issue list may already point to the same problem.
Discord webhook error codes and next steps
HTTP status and JSON code are separate fields, not interchangeable numbers. The table lists useful combinations and individual API codes; the actual response is the evidence. A status alone cannot tell you which field or resource failed.
| Response | What it identifies | Next check |
|---|---|---|
| 400 / 50035 | Payload or Content-Type validation | Read the nested field error; fix that field in the editor |
| 401 / 50027 | Webhook token problem | Copy the complete current URL from your webhook settings |
| 403 / 50013 | Insufficient permissions | Check the failed action and the app's access in that destination |
| 404 / 10015 | Webhook not found | Verify which saved webhook the send is using |
| 404 / 10008 | Message not found | Verify the original webhook, message link and thread target |
| 10003 | Channel not found | Check that the thread belongs to the webhook's parent channel |
| 429 | Rate limit | Wait for the indicated retry delay before trying again |
| 220001 / 220002 | Missing or conflicting forum destination | Choose a new post title OR an existing Thread ID |
Fix 400 Invalid Form Body in a Components V2 message
DWEEB's local validation catches supported layout and field constraints; Discord remains the final authority for current permissions, destination state and server-side rules. A clean local issue list narrows the investigation, but it cannot prove a remote post will succeed.
- Import the payload into the Discord message builder and resolve error-severity issues before another send.
- For a V2 design, place visible text inside Text Displays and remove legacy top-level content and embeds. Use the embed converter when the source is a legacy embed.
- Check that every Section has a button or thumbnail accessory, and inspect any empty media URLs, invalid link URLs or conflicting allowed-mentions settings named in the error.
- If your own code sends the export, use its JSON body as JSON. When uploading files with FormData, let the browser set the multipart Content-Type and boundary.
Why components disappear or buttons fail
When sending an exported design through your own webhook client, check the request URL as well as the payload. A person-created webhook needs with_components=true for Discord to respect its components. The JSON also needs the V2 flag. DWEEB's own send path supplies both; a script that copies only the components array can miss them.
A link button and a custom-ID button have different delivery requirements. If a static message works but an interactive control fails, review its setup badge and app-owned destination. A visually correct button does not create the application handler that responds to its click. The Components V2 guide explains the ownership boundary.
Diagnose restore and update separately from new sends
If sending a new message works but Restore fails, start with the message target. Copy its complete Discord link, select the webhook that originally posted it and inspect the optional Thread ID. Creating a new webhook does not transfer ownership of old posts. Follow the restore and update workflow before deciding to repost.
For forum and media channels, distinguish a new post title from an existing post's channel ID. In DWEEB, the Forum post fields create a post; the Send panel's Thread ID addresses an existing one. The forum and thread guide walks through both paths.
Handle rate limits, network failures and uncertain sends
On 429, wait for Discord's reported retry delay. DWEEB exposes that delay as a countdown. Stop other repeated sends using the same destination while you investigate, and consult the webhook limits guide for automation design.
A browser network error means DWEEB did not receive a readable response; it does not prove Discord rejected the message. Check the destination channel before sending again, then inspect connectivity and extensions that block discord.com. A lost response after a successful post can otherwise turn one intended announcement into duplicates.
When asking for help, share the operation, status, code and a redacted field error. Remove webhook tokens, message content and private identifiers from screenshots and network logs. Keep a local draft of the design so the diagnostic process does not become a rebuild.
Frequently asked questions
What does Invalid Form Body mean on a Discord webhook?
A field in the request failed Discord's validation. The response's errors object names the path — for example components.0.components.1.url is the URL on the second child of the first component — and a nested code says which rule it broke. Fix that one field and send again.
Why does my Discord webhook return 404?
For a new message, Unknown Webhook (10015): the webhook was deleted or the URL is wrong. When editing or deleting, Unknown Message (10008): the message ID is wrong, the message is gone, or it sits inside a thread and the request left out thread_id.
Why does my Discord webhook return 401 Invalid Webhook Token?
The token half of the URL is incomplete or out of date, often because the URL was cut off when copied or the webhook was deleted and recreated. Copy the whole URL again from the channel's Integrations settings.
What causes Missing Permissions (50013) with webhooks?
Sending through a webhook needs no permission — the token in the URL is the credential. The error comes from managing webhooks, which needs Manage Webhooks in that channel, or from an app acting in a channel where its role lacks the permission the action requires.
Why does Discord say “Cannot send an empty message” when I send components?
Discord ignored the components, so nothing visible was left. Webhooks respect the components field only when the URL includes with_components=true, and a Components V2 body also needs the flag 32768. Add the query parameter and check the flag.
How long should I wait after a 429 from a Discord webhook?
Exactly as long as Discord says: the retry_after value in the response body, also sent as a Retry-After header. Then resume at a slower pace and queue sends per webhook, so a burst does not trigger the limit again.
Put the guide into practice
Open the exact workflow in DWEEB. Nothing posts until you review and confirm it.
Check a webhook message visually →