---
title: "Errors"
description: "The error envelope and every stable error code."
canonical: https://docs.mentio.dev/errors
markdown: https://docs.mentio.dev/errors.mdx
---

# Errors

The error envelope and every stable error code.

{/* Generated by scripts/generate-api-docs.mjs from openapi.json (x-mentio-errors, the core error catalog). Do not edit: change the catalog in packages/core/src/errors.ts and regenerate. */}

All errors use one envelope with a stable machine-readable code and the id of the request:

```json
{
  "error": {
    "code": "validation_error",
    "message": "term: String must contain at least 2 character(s)",
    "requestId": "3f9c1e2a-7b4d-4c1e-9a0b-2d5e6f7a8b9c"
  }
}
```

`requestId` is also the `X-Request-Id` response header (every response has one, see [Conventions](/conventions#errors)); quote it when you write to support. A `rate_limited` error adds `retryAfterSeconds` and the `Retry-After` header. A 5xx is safe to retry with backoff; a 4xx is not, with the same payload.

## Codes

| Code                       | Status | When                                                                                                                                                              | What to do                                                                                                                    |
| -------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `unauthorized`             | 401    | No credential, or one that does not resolve to a workspace.                                                                                                       | Send Authorization: Bearer mk\_live\_… with a key from /api-keys.                                                             |
| `forbidden`                | 403    | The credential is valid but may not do this.                                                                                                                      |                                                                                                                               |
| `read_only_key`            | 403    | A read key was used on a write endpoint.                                                                                                                          | Create a key with write scope.                                                                                                |
| `validation_error`         | 400    | The body or query failed validation; the message names the field.                                                                                                 |                                                                                                                               |
| `not_found`                | 404    | No such resource in your workspace. An id from another workspace is a 404, never a 403.                                                                           |                                                                                                                               |
| `invalid_cursor`           | 400    | The cursor was not one this endpoint issued, or the filters changed between pages.                                                                                |                                                                                                                               |
| `payload_too_large`        | 413    | The body exceeds the endpoint limit.                                                                                                                              |                                                                                                                               |
| `rate_limited`             | 429    | The workspace made more than 600 requests in a minute (every key and token together), or hit an endpoint's own cap; Retry-After says when.                        | Wait Retry-After seconds and send the same request again. X-RateLimit-Remaining on every response says how much room is left. |
| `duplicate_keyword`        | 409    | The group already tracks this term. A term may be tracked once per group; another group may track it too.                                                         |                                                                                                                               |
| `insufficient_balance`     | 402    | The wallet cannot cover one more keyword-day.                                                                                                                     | Top up from Billing; tracking resumes on its own.                                                                             |
| `keyword_limit_reached`    | 402    | The self-serve keyword cap; beyond it is an enterprise conversation.                                                                                              |                                                                                                                               |
| `billing_not_configured`   | 503    | This deployment has no payment provider set up.                                                                                                                   |                                                                                                                               |
| `schedule_required`        | 400    | A daily alert rule was created without a schedule.                                                                                                                |                                                                                                                               |
| `unknown_channel`          | 400    | A rule referenced a channel id the workspace does not own.                                                                                                        |                                                                                                                               |
| `not_a_digest`             | 400    | A digest-only action was called on an instant rule.                                                                                                               |                                                                                                                               |
| `hourly_email_unsupported` | 400    | An hourly rule was given an email channel (up to 24 emails a day per rule). Use Slack, Telegram or a webhook, or a daily digest by email.                         |                                                                                                                               |
| `slack_not_connected`      | 409    | Connect Slack from Settings first. 404 on the Slack connection endpoints, 409 elsewhere.                                                                          |                                                                                                                               |
| `slack_not_configured`     | 503    | This deployment has no Slack app credentials.                                                                                                                     |                                                                                                                               |
| `telegram_not_configured`  | 503    | This deployment has no Telegram bot.                                                                                                                              |                                                                                                                               |
| `feed_unreadable`          | 400    | No feed was found at the address, or the site blocks automated readers, or it answers an error; the message says which.                                           |                                                                                                                               |
| `feed_login_required`      | 400    | The address answers a sign-in or access-denied page. Send it again with `auth` (the cookie your browser sends to the forum, or one header) to read it signed in.  |                                                                                                                               |
| `feed_login_expired`       | 400    | The feed still answers a sign-in page with the `auth` sent: copy a fresh cookie from a signed-in browser.                                                         |                                                                                                                               |
| `feed_limit_reached`       | 409    | A workspace reads at most 500 feeds; disconnect one before connecting another.                                                                                    |                                                                                                                               |
| `feed_auth_not_configured` | 503    | This deployment has no FEED\_AUTH\_SECRET, so a feed sign-in (auth) cannot be stored encrypted and is refused instead.                                            |                                                                                                                               |
| `email_not_configured`     | 503    | This deployment has no Resend key, so nothing that needs Resend (the audience sync) can run.                                                                      |                                                                                                                               |
| `invalid_assignee`         | 400    | The assignee id is not a workspace member.                                                                                                                        |                                                                                                                               |
| `classification_pending`   | 409    | A relevance or sentiment verdict was sent for a mention the classifier has not scored yet.                                                                        |                                                                                                                               |
| `invalid_member`           | 400    | A person's owner, and the member an outreach activity is credited to, must be workspace members.                                                                  |                                                                                                                               |
| `already_member`           | 409    | An invitation was sent to an address that already belongs to a member.                                                                                            |                                                                                                                               |
| `last_owner`               | 409    | Removing this member would leave the workspace without an owner.                                                                                                  |                                                                                                                               |
| `duplicate_segment`        | 409    | Segment names are unique per workspace.                                                                                                                           |                                                                                                                               |
| `filter_too_complex`       | 400    | The filters together (lists, anyOf groups, a view or alert rule applied on top) name more values than one query can carry. Shorten the lists or use fewer groups. |                                                                                                                               |
| `duplicate_view`           | 409    | View names are unique per workspace.                                                                                                                              |                                                                                                                               |
| `duplicate_group`          | 409    | Group names and externalIds are unique per workspace.                                                                                                             |                                                                                                                               |
| `default_group`            | 409    | Every workspace keeps one default group, where a keyword lands when no group is named.                                                                            |                                                                                                                               |
| `default_group_context`    | 400    | The default group is the workspace itself: its company is the profile (PATCH /v1/company). A company description goes on any other group.                         |                                                                                                                               |
| `group_changed`            | 409    | A keyword was created in or moved into the group while its keywords were being deleted. Its other keywords are gone; retry the delete.                            |                                                                                                                               |
| `invalid_signature`        | 401    | An inbound webhook did not carry a valid signature.                                                                                                               |                                                                                                                               |
| `webhook_not_configured`   | 503    | The inbound webhook has no secret on this deployment.                                                                                                             |                                                                                                                               |
| `invalid_token`            | 400    | A signed link (unsubscribe, confirmation) was tampered with or is malformed.                                                                                      |                                                                                                                               |
| `protected_user`           | 409    | Staff accounts cannot be deleted from the admin page.                                                                                                             |                                                                                                                               |
| `upstream_unavailable`     | 502    | A provider (Slack, Telegram, Polar, …) failed while serving your request. Retry with backoff.                                                                     |                                                                                                                               |
| `internal_error`           | 500    | Something failed on our side. The response carries a requestId; quote it to support.                                                                              |                                                                                                                               |

Handle errors by branching on `error.code`, not on the message; messages can change, codes will not. Codes are additive: treat one you do not know as a generic failure of the same status.
