# Errors

> Every error code the API returns, its HTTP status and what to do about it.

A failure always has the same shape, with the real HTTP status:

```json
{
  "ok": false,
  "error": "missing_scope",
  "message": "The app needs the “chat:write” permission.",
  "needed": "chat:write"
}
```

Compare `error`, which is stable. `message` is for people and can change. Some codes add fields.

| Code                     | Status | Meaning and what to do                                                           |
| ------------------------ | ------ | -------------------------------------------------------------------------------- |
| `invalid_auth`           | 401    | The token is missing, wrong or replaced. Use the current one                     |
| `missing_scope`          | 403    | The app lacks a permission. `needed` names it; ask an admin to add it            |
| `app_disabled`           | 403    | The app is deactivated                                                           |
| `not_in_channel`         | 403    | The app doesn't belong to that channel (or it doesn't exist: we don't say which) |
| `channel_not_public`     | 403    | Only public channels can be joined; a person adds the app to a private one       |
| `forbidden`              | 403    | The app isn't allowed to do that there                                           |
| `channel_not_found`      | 404    | The channel doesn't exist                                                        |
| `message_not_found`      | 404    | The message doesn't exist                                                        |
| `thread_not_found`       | 400    | `parent_id` isn't a top-level message of that channel                            |
| `webhook_not_found`      | 404    | The webhook URL is wrong or was replaced                                         |
| `response_url_invalid`   | 404    | A slash command's response URL is unknown or expired                             |
| `response_url_exhausted` | 429    | That response URL has been used 5 times                                          |
| `invalid_arguments`      | 400    | The request isn't valid. `details` lists `{ field, message }` for each problem   |
| `invalid_cursor`         | 400    | `cursor` isn't one this endpoint returned                                        |
| `invalid_json`           | 400    | The body isn't valid JSON                                                        |
| `invalid_content_type`   | 415    | Send `Content-Type: application/json`                                            |
| `request_too_large`      | 413    | The body is over 64 KB                                                           |
| `message_id_in_use`      | 409    | That `id` belongs to another message                                             |
| `ratelimited`            | 429    | Too many requests. Wait `Retry-After` seconds                                    |
| `internal_error`         | 500    | Our fault. Try again in a moment; it is logged on our side                       |

Retry `429` and `5xx` with backoff, and nothing else without changing the request. New codes may be
added: treat an unknown one like its status class.

