Skip to content

Errors

A failed HTTP API request returns a JSON error body with a stable machine-readable code and a human-readable message. Match on code; show message to a person if you like, but never parse it.

Match on code, not on message or the status alone

message is written for people. It may be localized (most messages are currently in Turkish) and its wording can change without notice. Many different codes share one HTTP status, so the status alone is not enough either.

Error response

Field Type Description
code string Stable error code from the error code registry
message string Human-readable description; may be localized
error string The HTTP status line, such as "404 Not Found". Kept for older clients; prefer code

A failure with more to report adds members next to these at the top level: a rate limit denial adds global and retry_after, a validation failure can add errors, and a few operations add their own members as documented on their pages.

{
  "error": "404 Not Found",
  "code": "NOT_FOUND",
  "message": "Kanal bulunamadı"
}

Responses without a JSON body

A request to a path that matches no route returns 404 with an empty body. A request body that is not valid JSON, or does not match the operation's field types, is rejected by the HTTP framework with 400 and a plain-text body. Clients should handle a 4xx or 5xx response whose body is empty or not JSON.

Status codes

Status Meaning
200 Success with a JSON body
204 Success with no body
400 The request was malformed, failed validation or a precondition
401 The credential is missing, malformed, unknown, expired or revoked. See Authentication
403 The caller is authenticated but not allowed to perform the operation
404 The resource does not exist or is not visible to the caller
409 The request conflicts with the current state, such as a name or address already in use
410 The operation has been removed
413 The request body is too large
429 A rate limit was exceeded
500 An internal failure, or a server-side feature that is not configured

A 5xx response is not the caller's fault. Retry it later with backoff. A 500 always carries the generic message and never includes internal details.

Error code registry

These are the values the top-level code field can take. Most failures use one of the generic codes; operations that need a more precise answer use a specific code.

Generic codes

Code Status Description
BAD_REQUEST 400 The request was malformed or failed a precondition
INVALID_FORM_BODY 400 One or more fields failed validation. See Validation errors
UNAUTHORIZED 401 The credential is missing or invalid
FORBIDDEN 403 The caller may not perform the operation, for example a missing permission, a member who is timed out, or a non-member
NOT_FOUND 404 The resource does not exist or is not visible
CONFLICT 409 The request conflicts with existing state
PAYLOAD_TOO_LARGE 413 The request body exceeds the allowed size
RATE_LIMITED 429 A rate limit bucket is exhausted. See Rate limits
INTERNAL_SERVER_ERROR 500 An unexpected internal failure

Specific codes

Code Status Description
SLOWMODE_RATE_LIMITED 400 The channel's slowmode interval has not elapsed. The body has retry_after. See Slowmode
CAPTCHA_REQUIRED 400 The operation needs a CAPTCHA solution. See CAPTCHA handling
INVALID_CAPTCHA 400 The CAPTCHA solution was rejected or could not be verified
SUDO_MODE_REQUIRED 403 The operation needs recent re-verification. See Sudo mode. The error member of this body is "SUDO_REQUIRED"
IP_AUTHORIZATION_REQUIRED 403 A login from a new location must be confirmed by email. The body has ticket, email, ip_authorization_required and resend_available_in
MISSING_PERMISSIONS 403 The caller lacks a guild permission the operation checks explicitly, such as moving a member between voice channels or opening the member search
MISSING_ACCESS 403 The caller may not access the target, such as a user whose privacy settings block calls or group invitations from the caller, or a staff-only field
MISSING_OAUTH_SCOPE 403 An OAuth2 access token lacks the scope the operation needs. The body has required_scope. See OAuth2 access tokens
UNKNOWN_USER 404 The target user does not exist
UNKNOWN_MEMBER 404 The target user is not a member of the guild
NO_USERS_WITH_TEBHOSTAG_EXIST 404 No user matches the username and discriminator in a friend request
ALREADY_FRIENDS 400 The users are already friends
CANNOT_SEND_FRIEND_REQUEST_TO_SELF 400 The target of a friend request is the caller
CANNOT_SEND_FRIEND_REQUEST_TO_BLOCKED_USER 400 The caller has blocked the target
FRIEND_REQUEST_BLOCKED 400 The target's settings do not accept friend requests from the caller
MAX_FRIENDS 400 The caller has reached the relationship limit
MAX_APPLICATIONS 400 The caller already owns 25 applications. See Create application
CANNOT_SEND_MESSAGES_TO_USER 400 The target's privacy settings block direct messages from the caller
USER_NOT_IN_VOICE 400 The target member is not connected to a voice channel
INVALID_OR_RESTRICTED_RTC_REGION 400 The requested voice region does not exist or is not available
MEMBER_ROLE_ENUM_REMOVED 410 A removed legacy member-role operation was called. The message names the replacement route

The message for each code describes the specific situation. Operation pages list the codes each operation can return.

Validation errors

A field-level validation failure returns 400 with code set to INVALID_FORM_BODY. It comes in two forms.

With an errors array. When a failure has a machine-readable reason, the body names the field and the reason:

{
  "error": "400 Bad Request",
  "code": "INVALID_FORM_BODY",
  "message": "Hakkımda çok sık değiştirildi; 12 dakika sonra yeniden dene.",
  "errors": [
    {
      "path": "bio",
      "code": "BIO_CHANGED_TOO_MANY_TIMES",
      "message": "BIO_CHANGED_TOO_MANY_TIMES"
    }
  ]
}
Field Type Description
path string The request field that failed, such as channel_id
code string A stable validation code from the table below
message string Currently repeats code

Without an errors array. Other validation failures return INVALID_FORM_BODY with only a descriptive message, which names the field and constraint in text.

Validation codes

Code Field Description
CHANNEL_DOES_NOT_EXIST channel_id The target channel of a member voice move does not exist
CHANNEL_MUST_BE_VOICE channel_id The target channel of a member voice move is not a voice channel
USER_NOT_IN_CHANNEL recipients A user named in a call ring request is not a recipient of the channel
BIO_CHANGED_TOO_MANY_TIMES bio The biography was changed too often (25 changes per 30 minutes)
PRONOUNS_CHANGED_TOO_MANY_TIMES pronouns The pronouns were changed too often
ACCENT_COLOR_CHANGED_TOO_MANY_TIMES accent_color The accent colour was changed too often
AVATAR_CHANGED_TOO_MANY_TIMES avatar The avatar was changed too often
BANNER_CHANGED_TOO_MANY_TIMES banner The banner was changed too often
INVALID_MESSAGE_DATA message_data A JSON message body does not match the message schema. See Embed limits
TOO_MANY_EMBEDS embeds A message, message edit, or webhook execution supplies more than 10 embeds
EMBEDS_EXCEED_MAX_CHARACTERS embeds The supplied embeds hold more than 6,000 characters in total

The profile limits apply both to the account profile and to per-guild member profiles. See Rate limits.

Rate limit errors

A rate limit denial returns 429 with code set to RATE_LIMITED and two extra members:

{
  "error": "429 Too Many Requests",
  "code": "RATE_LIMITED",
  "message": "Hız sınırlamasına takıldın. 1 saniye sonra tekrar dene.",
  "global": false,
  "retry_after": 0.428
}

retry_after is the delay in fractional seconds before one more request is admitted, and global tells whether the global limit produced the denial. Rate limits describes the headers and buckets.

OAuth2 errors

The OAuth2 operations use the same error body. Their message is often a short OAuth2-style token such as invalid_token, but clients should still switch on code.

Handling errors

  • Switch on code. Fall back to the HTTP status only for a response without a JSON body.
  • On 401, stop using the credential and obtain a new one.
  • On 429, wait retry_after seconds before retrying.
  • On 5xx, retry with exponential backoff.
  • On any other 4xx, change the request before sending it again.
const res = await fetch("https://tebhos.com/api/v1/channels/1489002177550843904", {
  headers: { Authorization: `Bot ${process.env.TEBHOS_BOT_TOKEN}` },
});

if (!res.ok) {
  const body = await res.json().catch(() => null);
  switch (body?.code) {
    case "RATE_LIMITED":
      await new Promise((r) => setTimeout(r, body.retry_after * 1000));
      break;
    case "NOT_FOUND":
      console.warn("Channel not found or not visible");
      break;
    default:
      throw new Error(`${res.status} ${body?.code ?? "UNKNOWN"}`);
  }
}
import os, time, requests

res = requests.get(
    "https://tebhos.com/api/v1/channels/1489002177550843904",
    headers={"Authorization": f"Bot {os.environ['TEBHOS_BOT_TOKEN']}"},
)

if not res.ok:
    try:
        body = res.json()
    except ValueError:
        body = {}
    code = body.get("code")
    if code == "RATE_LIMITED":
        time.sleep(body["retry_after"])
    elif code == "NOT_FOUND":
        print("Channel not found or not visible")
    else:
        raise RuntimeError(f"{res.status_code} {code}")