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.
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_afterseconds 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}")