Error codes
Failed calls return a 4xx or 5xx status and a common error object. Programs decide what to do from the HTTP status, code and details; message is a human-readable explanation whose wording may change, so don't branch on it.
| You want to | See |
|---|---|
| Understand the error object's fields | Error response structure |
Look up what a code means | Error code list |
Look up what a details.reason means | details values |
| Decide whether to retry | Error handling recommendations |
The language of message is chosen with the Accept-Language request header (Simplified Chinese, Traditional Chinese or English), and responses carry Content-Language and Vary: Accept-Language. Language doesn't affect error codes, permissions or whether to retry, see Languages and localization.
Error response structure
{
"error": {
"code": "rate_limited",
"message": "群消息过于频繁,请稍后再试",
"details": { "reason": "group_send_rate" },
"request_id": "GNBHUXHA426Q3YLRLCJOWY7ISK"
}
}| Field | Type | Description |
|---|---|---|
error.code | String | Error code, see Error code list |
error.message | String | Human-readable explanation, in the language described above. The same code may have different messages in different cases, such as "user not found" and "API not found" for 404 not_found |
error.details | Object | Extra information, only for some errors and omitted otherwise. Most often reason, giving the specific cause, see details values |
error.request_id | String | This request's ID, the same as the X-Request-ID response header; provide it when troubleshooting |
Error responses may also carry these headers:
| Header | Description |
|---|---|
X-Request-ID | On every response, including successful ones. Echoed if the request carried a valid X-Request-ID, otherwise generated by the server, see Request headers |
Retry-After | On 429: seconds to wait, an integer of at least 1 |
Allow | On 405: the methods this path supports, such as POST |
Failures of individual items in batch APIs don't use this structure: the whole request returns 200 OK, and failed items each carry code, message and (sometimes) details, with the same values as on this page. For example when batch adding group members:
{
"changed": false,
"member_version": 3,
"results": [
{ "username": "e-u4", "code": "permission_denied", "message": "该用户在群黑名单中", "details": { "reason": "group_blacklisted" } },
{ "username": "alice", "code": "limit_exceeded", "message": "群人数已达上限", "details": { "reason": "group_member_limit" } }
]
}Error code list
These are all the error codes OpenAPI can return. Cases specific to an API are in the "Errors" section of its documentation.
| HTTP status | code | Meaning |
|---|---|---|
| 400 | invalid_argument | A parameter is missing, malformed or out of range; the body isn't a single JSON object, Content-Type isn't application/json, or there are unknown fields or wrong field types; invalid pagination cursor. message names the parameter, and some APIs give a reason in details |
| 401 | unauthenticated | Missing Authorization header, or the App Token is invalid, expired or no longer works. Get a new App Token and retry; don't retry when details.reason is app_deleted or tenant_closed, see Using and caching App Tokens |
| 401 | invalid_client | Wrong Client ID or Client Secret when getting an App Token, or org_name or app_name doesn't exist (including deleted apps and closed tenants) |
| 403 | permission_denied | The App Token doesn't belong to the app in the path; or business rules don't allow the operation, such as adding a user on the group blocklist to the group, with the reason in details.reason |
| 403 | ip_not_allowed | The source IP isn't in the app's IP allowlist |
| 403 | tenant_unavailable | The tenant is suspended or in the account-deletion cooling-off period, see App and tenant status |
| 403 | app_unavailable | The app is disabled or suspended; or the app is read-only and can't perform this write |
| 403 | user_disabled | The user is banned and can't be issued sign-in credentials; or sending as a user banned by the platform, with details.disabled_by platform |
| 403 | not_group_member | The user isn't a member of the group, such as sending a group message as a non-member or setting a non-member's group role; when setting group do-not-disturb for a user, the user isn't a member or the group was dismissed |
| 403 | group_disabled | The group is banned and the operation (such as adding members) isn't allowed; details.disabled_by is tenant (banned by you) or platform (banned by the platform) |
| 403 | chatroom_disabled | The chat room is banned and the operation (such as sending messages) isn't allowed; details.disabled_by is tenant (banned by you) or platform (banned by the platform) |
| 403 | message_rejected | The message failed pre-send checks and wasn't sent or saved: rejected by moderation or by your sync callback. Can be returned by sending messages, editing messages, batch sending (as each recipient's result) and sending chat room messages, with the reason in details.reason |
| 403 | content_rejected | Profile or request content (such as a nickname, group name, or friend request message) failed moderation and wasn't written; details.field names the field, see Moderation |
| 403 | file_blocked | The file was blocked for violations and can't be downloaded or deleted by you. Returned when deleting a blocked file, and as an item result when getting download addresses, see Download and manage files |
| 404 | not_found | The resource doesn't exist, such as a user, group, conversation, message or chat room that doesn't exist or was deleted; details.reason is chatroom_dismissed for dismissed chat rooms; when message is "API not found", the request path is wrong |
| 405 | method_not_allowed | The path exists but doesn't support this method; the Allow header lists supported methods |
| 409 | conflict | Resource or idempotency state conflict, see Standalone RTC channels |
| 409 | already_exists | The resource to create already exists, such as a taken username |
| 409 | version_conflict | The version in the request differs from the current one because another request changed the data, see Concurrent changes and version; for uploads, the upload's current state doesn't allow the operation, see File APIs |
| 409 | limit_exceeded | A count limit was exceeded, such as friends, group members or the app's storage quota, with which one in details.reason; no details when the app's registered users are full. App-level limits such as registered users, total groups and storage capacity are set by the app's plan |
| 409 | stream_in_progress | AI conversations: the conversation already has a reply being generated, with its ID in details.stream_id; you can end it first with replace |
| 409 | stream_closed | The AI reply has ended (stopped by the user, timed out, replaced, etc.) and can't be written to; details.reason is the final status and details.finish_reason the finish reason. Stop calling the model when you get this |
| 409 | seq_gap | AI reply delta sequence numbers aren't consecutive, or last_seq at finish doesn't equal the last accepted delta; details.expected_seq is the next expected number |
| 410 | file_expired | The file has expired or been deleted. Returned as an item result when getting download addresses; about 30 days after deletion the same file address returns not_found instead |
| 413 | payload_too_large | The body exceeds 1 MiB; or the message exceeds the app's message size limit, or a file's size at upload creation exceeds the limit for its purpose and type, with the limit in bytes in details.max_bytes |
| 429 | rate_limited | Too many requests, over the call quota or a rate limit; wait for the Retry-After header and retry, see Rate limits and app status |
| 500 | internal | Internal error, with the cause not returned to the caller. Retry later (waiting for Retry-After if present), and contact us with the request_id if it persists |
| 503 | unavailable | Temporarily unavailable; wait for Retry-After and retry, see Standalone RTC channels |
details values
Fields in details vary by error code; below are the values OpenAPI can return. New values may be added; handle unrecognized ones by code.
Tokens and service status
code | details | Description |
|---|---|---|
unauthenticated | reason is app_deleted | The app was deleted; the App Token is permanently invalid and getting a new one fails too. Stop calling and clear the stored credentials |
unauthenticated | reason is tenant_closed | The tenant closed its account; handle as above |
tenant_unavailable | reason is arrears | The tenant is suspended only for arrears and recovers once paid, see Overdue invoices and arrears |
app_unavailable | reason is arrears | The tenant is in arrears and the app is read-only: queries work but writes adding data don't, until paid, see Overdue invoices and arrears |
tenant_unavailable and app_unavailable without details mean the tenant or app is disabled, suspended, or set read-only by the platform; check the tenant and app status in the console, see App and tenant status.
Count limits
details.reason of limit_exceeded; for values see Limits. The app's total groups, storage capacity and chat rooms (and registered users, which come without details) are set by the app's plan, and members per group and groups per user also don't exceed the plan's quotas, see Plan quotas:
details.reason | Description |
|---|---|
friend_limit | The user's friends have reached the limit |
peer_friend_limit | The other user's friends have reached the limit |
blacklist_limit | The user's blocklist has reached the limit |
group_member_limit | The group's members have reached the limit |
user_group_limit | The groups the user has joined have reached the limit |
app_group_limit | Undismissed groups in the app have reached the plan's quota; no more groups can be created |
admin_limit | Admins of the group or chat room have reached the limit |
group_blacklist_limit | The group blocklist has reached the limit |
pinned_message_limit | Pinned messages in the conversation have reached the limit |
edit_limit | This message's edits have reached the limit |
storage_quota | The app's storage usage plus this upload exceeds the storage quota (the smaller of the platform quota and the plan's storage capacity), also with limit_bytes (quota) and used_bytes (current usage) in bytes |
group_file_limit | The group already has 1000 files |
chatroom_limit | Undismissed chat rooms in the app have reached the limit (the smaller of the platform quota chatroom_limit and the plan's chat room count); no more chat rooms can be created |
mute_limit | The chat room mute list has 10,000 entries, returned as an item result in batch APIs |
ban_limit | The chat room ban list has 10,000 entries, returned as an item result in batch APIs |
allowlist_limit | The chat room allowlist has 500 people, returned as an item result in batch APIs |
attribute_limit | After the write, chat room attributes would exceed the count or total size limit; returned as the reason for keys not written in the result of setting attributes |
conversation_setting_limit | The user already has 10,000 conversation do-not-disturb settings |
word_list_limit | The app already has 20 word lists |
word_limit | Adding would exceed 10,000 words in the list or 50,000 in the app; nothing in the batch was added |
penalty_rule_limit | More than 5 automatic penalty rules |
app_bots | The app's AI bots have reached the limit (100) |
bot_active_streams | This bot's concurrent replies have reached its limit (the bot's max_active_streams) |
Rate limits
details.reason of rate_limited; the wait time is always in the Retry-After header:
details.reason | Description |
|---|---|
No details | The app's OpenAPI call quota is used up, or another rate limit was exceeded (such as getting App Tokens, password hashing) |
group_send_rate | This group's messages per second have reached the limit |
app_message_rate | The app's messages per minute have reached the quota |
image_busy | Image processing was busy when completing the upload; the file is still uploading, so just call complete again later |
room_send_rate | Messages per second in this chat room, or across all the app's chat rooms, have reached the limit. Only returned for normal and high priority; low-priority messages over the limit are dropped without an error |
room_attribute_rate | This chat room's attribute writes have reached 20 a second |
request_rate | Reporting on users' behalf exceeded 600 a minute per app |
ai_write | The app's AI reply deltas written per second have reached 5000 |
Permissions
details.reason of permission_denied:
details.reason | Description |
|---|---|
group_blacklisted | The user is on the group blocklist and can't join the group |
not_editable | Only text and custom messages can be edited |
message_recalled | The message was recalled or erased and can't be edited or pinned |
owner_protected | The chat room owner can't be muted, removed or banned; returned as an item result in batch APIs. Transfer or clear the owner first |
target_banned | The user to be made a chat room admin is banned from this chat room |
not_revisable | The review item's current state doesn't allow deciding, revising or escalating, such as changing no violation to violation, revising after 30 days, or an item already escalated or closed, see Decide |
app_rejected | A client's friend request or group join was rejected by your before-add-friend or before-join-group callback; message is your server's prompt, also with app_reason, see Sync callbacks |
callback_unavailable | The before-add-friend or before-join-group callback failed (timeout, receiver failure, etc.) and the operation was refused per the failure policy, also with app_reason (null), see Sync callbacks |
Unbanning or dismissing a chat room banned by the platform returns permission_denied without details; such chat rooms can only be handled by the platform.
Message API parameter errors
When message APIs return invalid_argument, details.reason gives the cause:
details.reason | Description |
|---|---|
unknown_type | Unsupported message type |
invalid_body | The message body doesn't meet the type's requirements; details.field names the field |
invalid_client_msg_id | client_msg_id isn't 1 to 64 visible ASCII characters |
reserved_client_msg_id | client_msg_id starts with the reserved evt_ or sys_ |
invalid_recipient | Not exactly one of to_user and to_group was given |
from_required | from is missing when sending one-to-one messages or batch sending |
self_conversation | Sending a message to yourself |
mentions_not_allowed | @mentioning users in a one-to-one chat |
invalid_reply | The quoted message doesn't exist, isn't visible or was recalled; or quoting in a batch send |
receipt_not_allowed | Requesting read receipts in a one-to-one chat, or when sending as the system |
invalid_online_only | An online-only message isn't of type custom, or has fields that aren't allowed |
invalid_push | push isn't a JSON object, has fields other than disabled, force, title, body and ext, has invalid values (such as disabled and force both true), or exceeds 2 KB; details.field names the field, see The push field when sending messages |
too_many_items | Too many items in one submission; details.field is the parameter and details.max the limit |
empty_edit | Neither body nor ext given when editing a message |
conflicting_params | More than one of after_seq, before_seq and around_seq given when fetching messages |
File APIs
When the upload files and download and manage files APIs return invalid_argument, details.reason gives the cause:
details.reason | Description |
|---|---|
invalid_purpose | This API can't upload this purpose, or the purpose doesn't match the type kind |
invalid_name | The file name is empty or invalid |
unsupported_type | When completing the upload, the format detected from the content doesn't match the type; details.content_type is the detected format |
size_mismatch | The uploaded content's size differs from what was declared when creating the upload. Upload the content again, then complete |
upload_incomplete | The content hasn't been uploaded, or parts are missing or the wrong size. Complete after the upload is whole |
image_too_large | The image exceeds 50 megapixels |
invalid_image | The image can't be decoded |
upload_failed | This upload already failed for reasons such as a format mismatch or the group's files being full; completing again won't succeed, so create a new upload |
details.reason of version_conflict:
details.reason | Description |
|---|---|
upload_completed | The upload is already complete and can't be canceled, have part upload addresses requested, or have uploaded parts queried. To delete a completed file, call delete file |
upload_changed | The content was uploaded again while completing. Just call complete again |
upload_completing | Another complete request is processing this file; retry later |
Chat room API parameter errors
When chat room APIs return invalid_argument, details.reason gives the cause. When sending chat room messages, message body errors are the same as for message APIs (such as unknown_type, invalid_body):
details.reason | Description |
|---|---|
invalid_priority | priority isn't one of high, normal, low |
invalid_attribute_key | An attribute key is invalid or duplicated; details.key names it |
invalid_attribute_value | An attribute value isn't a string, exceeds 4096 bytes or contains control characters; details.key names the key |
attributes_too_large | Keys and values set at once exceed 16 KB in total; details.max_bytes is the limit |
auto_delete_not_allowed | Attributes set by the server can't be marked auto_delete |
Push API parameter errors
When the push devices and settings APIs return invalid_argument, details.reason gives the cause:
details.reason | Description |
|---|---|
invalid_duration | The do-not-disturb duration isn't between 1 and 315360000 seconds (10 years) |
invalid_quiet_hours | The quiet hours times are malformed, or start equals end |
invalid_timezone | The time zone isn't an IANA time zone name |
invalid_preview | The preview mode isn't full, sender_only or none |
invalid_mode | The conversation do-not-disturb mode is missing or invalid, or mention_only was set for a one-to-one chat |
self_conversation | The other party of one-to-one do-not-disturb is the user themself |
Moderation
When content fails moderation, message APIs return 403 message_rejected and profile and request APIs return 403 content_rejected, with these details values:
details | Description |
|---|---|
reason is sensitive_content | Matched a word list set to reject |
reason is provider_block | Third-party moderation judged it a violation; with avatar review on, the avatar address isn't publicly reachable or is a blocked file |
reason is check_timeout | Deprecated: third-party review only runs after content is sent or saved, so this is no longer returned |
field | Only in content_rejected: the rejected field, the first one when several were rejected: nickname, avatar_url for user profiles; name, description, announcement, avatar_url for group profiles; group_nickname for group nicknames; message, reason for join requests; message, add_source for friend requests; name, description, announcement for chat room profiles |
In batch sends, a rejected message returns message_rejected in every recipient's result; in batch user creation, rejected users appear in failed with code content_rejected and no details. Retrying the same content won't succeed for either; prompt the user to change it.
When the moderation config and word lists and review items and violations APIs return invalid_argument, values of details.reason:
details.reason | Description |
|---|---|
unsupported_scene | The scene doesn't support that review mode, such as post-send third-party text review in scenes other than messages and chat room messages |
provider_account_required | Third-party moderation was turned on, but the app hasn't chosen a moderation account in the console |
unsupported_action | A block list without an action, or an allow list with one |
invalid_action | An action in the decision doesn't apply to the target, such as recalling a message on a user profile; or a no-violation decision with actions |
self_report | When reporting on a user's behalf, the reporter reported themself or their own message |
invalid_evidence | When reporting on a user's behalf, the evidence messages don't qualify: evidence attached when not reporting a user or group; or an evidence message doesn't exist, isn't visible to the reporter, was recalled, wasn't sent by the reported user, or isn't in the reported group |
Sync callbacks
When an operation is rejected by your sync callback, the before-send and before-chat-room-send callbacks return 403 message_rejected and the before-add-friend and before-join-group callbacks return 403 permission_denied, with the same details:
details | Description |
|---|---|
reason is app_rejected | Your server rejected the operation; message is your prompt ("message failed to send" or "operation rejected" if you gave none) |
reason is callback_unavailable | The sync callback failed (timeout, connection failure, invalid response, etc.) and was handled as "reject" per your failure policy, see Handling failures |
app_reason | The reason code you gave in the response; null if you gave none, and for callback_unavailable |
By default sync callbacks only run for users' actions in clients. When your server sends messages (including chat room messages) or edits messages, the corresponding sync callback only runs if its vias condition includes server actions; your server adding friends or adding group members never runs the before-add-friend or before-join-group callbacks. Batch sends call the callback once, and if rejected every recipient's result is message_rejected.
AI conversations
AI conversation APIs and bot accounts:
code | details.reason | Description |
|---|---|---|
invalid_argument | not_bot | bot isn't a bot when starting a reply |
invalid_argument | recipient_is_bot | The recipient is a bot when starting a reply |
invalid_argument | invalid_ops | Malformed delta: a skipped part index, appending to a non-text part, or more than 200 parts |
permission_denied | bot_disabled | The bot is disabled and can't start replies |
permission_denied | bot_account | An operation not allowed on bots: issuing sign-in credentials, changing passwords, adding a bot to a group |
payload_too_large | line / content | One delta exceeds 64 KB; or one reply's writes exceed 1 MB in total (the reply also ends as failed) |
Other fields
code | details | Description |
|---|---|---|
not_group_member | reason is leave_pending | When reading or acting on a group conversation as a user who just left the group, processing isn't finished yet. Messages from before they left are usually readable within seconds; retry later |
internal | reason is dependency_unavailable | A dependent service is temporarily unavailable; wait for the Retry-After header and retry: when querying presence (5 to 30 seconds); when querying chat room members, removing chat room members, sending and recalling chat room messages, or querying recent chat room messages (1 to 5 seconds). A chat room message that got this wasn't sent; retry with the same client_msg_id |
user_disabled | disabled_by is platform | The user is banned by the platform, so your server can't send messages (including chat room messages) as them; users banned by you still can |
group_disabled | disabled_by is tenant or platform | Who banned the group: tenant is you (through OpenAPI or the console), platform the platform |
chatroom_disabled | disabled_by is tenant or platform | Who banned the chat room, as above |
not_found | reason is chatroom_dismissed | The chat room was dismissed. Its information can still be queried for 30 days after dismissal while other APIs return this error, which lets you tell "dismissed" from "never existed" |
payload_too_large | max_bytes | The app's message size limit, or the file size limit for this purpose, in bytes |
invalid_argument | field | The field in error, such as a field in the message body, or usernames in a presence query |
invalid_argument | reason is invalid_date_range | When getting call statistics, from or to is missing, isn't YYYY-MM-DD, to is before from, or the range exceeds 92 days |
Console and client API error codes
These error codes are never returned by OpenAPI; they only appear in the console and client APIs (called by users with a User Token), listed here for reference:
| HTTP status | code | Meaning |
|---|---|---|
| 400 | invalid_code | Wrong or expired verification code, two-factor code or recovery code |
| 401 | invalid_credentials | Wrong account or password, or the user's sign-in credential is invalid, used or expired |
| 401 | mfa_required | Console sign-in also needs a two-factor code |
| 401 | session_revoked | The user's sign-in session was revoked, such as by signing in on another device, being signed out, or changing the password; details.reason gives why, see Sign-out reasons |
| 403 | reauth_required | Sensitive console operations (such as adding push credentials or creating callback endpoints) need re-verification first |
| 403 | captcha_required | A CAPTCHA must be completed first |
| 403 | mfa_setup_required | The tenant requires two-factor authentication for everyone and this account hasn't set it up |
| 403 | invitation_target_mismatch | The current account doesn't match the invited email or phone number |
| 403 | user_muted | The user is globally muted and can't send |
| 403 | user_blocked | Not allowed because of the blocklist. For friend requests details.by is self (you blocked them) or peer (they blocked you); for one-to-one messages it's always peer |
| 403 | friend_add_denied | The other user doesn't allow anyone to add them |
| 403 | not_friend | The app only allows messages between friends, or only allows inviting friends to groups, and they aren't friends |
| 403 | group_muted | Muted in the group, or the group is muted for everyone |
| 403 | not_chatroom_member | The user's connection isn't in the chat room, such as sending chat room messages or setting attributes without entering |
| 403 | chatroom_muted | Muted in the chat room (details.reason member, with the expiry in details.muted_until, null for permanent), or the chat room is muted for everyone (details.reason all) |
| 409 | owner_transfer_required | Ownership must be transferred first, such as when leaving the tenant or a group owner leaving the group |
| 409 | slot_unavailable | Both Secret slots are taken and one must be revoked first; when rotating a callback signing key, the old key's grace period hasn't ended, see Rotating keys |
| 409 | last_active_secret | The app's last valid Secret can't be revoked |
| 409 | device_limit_exceeded | Signed-in devices have reached the limit and the app is set to reject new devices |
| 409 | call_in_progress | You're already in a call and can't start, answer or join: details.reason is in_other_call (in another call, with it in details.call_id) or joined_on_other_device (already answered or joined this call on another of your devices) |
| 409 | call_ended | The call has ended and can't be answered, joined, invited to, renewed or heartbeated; details.end_reason and details.ended_at give the reason and time |
| 410 | invitation_expired | The invitation expired, was revoked or was already accepted |
| 429 | too_many_attempts | Too many failed sign-ins; sign-in is temporarily restricted |
In client and console APIs, common error codes may also carry these details.reason values, never returned by OpenAPI:
code | details.reason | Description |
|---|---|---|
limit_exceeded | app_connection_limit | When opening a persistent connection, the app's online connections have reached the plan's concurrent connection quota, so the new connection is closed; existing connections aren't affected. The disconnect message includes a 30-to-60-second wait, after which the client reconnects |
limit_exceeded | chatroom_member_limit | The chat room is full and can't be entered |
limit_exceeded | connection_room_limit | This persistent connection is already in 10 chat rooms and can't enter more |
limit_exceeded | call_full, call_member_limit | The group call is full; the group call has involved 100 members |
limit_exceeded | app_concurrent_calls | The app's ongoing calls have reached the plan's concurrent call quota, with the quota in details.limit, see Concurrent call quota |
limit_exceeded | credential_limit, template_limit | Console: the app already has 20 push credentials; notification template overrides already number 300 |
limit_exceeded | endpoint_limit, rps_limit | Console: the app already has 5 callback endpoints; the endpoint's requests per second exceed the app's limit, with the limit in details.max |
permission_denied | chatroom_banned | The user is banned from this chat room and can't enter; details.expires_at is the expiry, null for permanent |
permission_denied | priority_denied | Only the chat room owner and admins can send high-priority messages |
permission_denied | role_required | Requires the chat room owner or admin role; details.required_role is the role needed |
permission_denied | admin_protected | Chat room admins can't act against other admins |
permission_denied | set_by_higher_role | The mute or ban was set by the owner or the server, so admins can't change or lift it |
permission_denied | client_create_disabled | The app doesn't allow clients to create chat rooms |
permission_denied | recall_window_expired, recall_denied | The chat room message is past the recall window; no permission to recall this chat room message |
permission_denied | attribute_owned | The chat room attribute was set by someone else, so ordinary users can't change or delete it; returned per key in the results of setting and deleting attributes |
permission_denied | rtc_disabled | The app doesn't have audio and video on: the runtime policy rtc_enabled is off, or the app's plan doesn't include audio and video |
permission_denied | rtc_suspended | The platform suspended this app's audio and video; details.suspend_reason gives why |
permission_denied | rtc_not_configured | Audio/video service temporarily unavailable |
permission_denied | not_host, not_in_call, not_joined_device | Ending a group call without being the starter, group owner or admin; inviting others without being in the call; renewing the media credential from a device that isn't in the call |
permission_denied | media_provider_unsupported | The client doesn't support the current media service; prompt the user to update the app |
permission_denied | report_disabled | The app turned off client reporting (runtime policy report_enabled); reporting on users' behalf isn't affected |
permission_denied | platform_disabled | Console: the push credential or callback endpoint was disabled by the platform and only the platform can restore it |
version_conflict | member_state | Your state in the call doesn't allow this operation, such as declining after answering; details.member_state is your current member state (null if never invited) |
invalid_argument | invalid_ext | The ext when entering a chat room or starting a call is invalid |
invalid_argument | invalid_target, self_call and others | Call operation parameter errors, see What clients need to do |
invalid_argument | unknown_credential, platform_mismatch and others | Client push registration failed, see Clients registering push tokens |
invalid_argument | url_not_allowed | An avatar, chat room cover or attachment address submitted by a client doesn't meet the runtime policy media_url_only; console: the callback endpoint URL doesn't meet requirements, with the reason in details.rule, see Address requirements |
invalid_argument | unknown_event_type, invalid_filter, invalid_condition | Console: the callback endpoint subscribes to an unknown event type (named in details.event_type); an invalid filter; an invalid sync callback condition |
invalid_argument | endpoint_unverified, endpoint_disabled, endpoint_mismatch | Console: the callback endpoint hasn't passed URL verification; the endpoint was disabled automatically and must be restored first; the sync callback is bound to an endpoint that isn't this app's. See Callback configuration |
Client APIs also have per-user rate limits, returning 429 rate_limited with details.reason of request_rate (per-minute or per-day counts), pair_daily_limit (times per day one person sends friend requests to the same person, or applies to join the same group), declined_cooldown (no new friend or join request within 24 hours of being declined; for group invitations, only when the app turns on the invitation cooldown, no re-invitation within 24 hours of leaving or declining), group_join_rate (this group's joins per minute are full), the message-sending user_send_rate, new_conversation_rate, mention_rate, conversation_send_rate and peer_receive_rate, chat room join_rate (entries per connection per minute), room_join_rate (this chat room's entries per second are full) and attribute_rate (attribute sets per user per minute), and the call operation call_rate, pair_call_rate, group_call_rate, invite_rate, signal_rate, token_rate and query_rate (see Audio and video calls). Sending test pushes too often in the console gives test_rate; callback URL verification and test sends beyond 20 a minute per app come without details.
Your server acting on users' behalf isn't subject to most of these; for example sending messages as a user doesn't check mutes, blocklists or friendships, see Send messages.
Error handling recommendations
Decide whether to retry by the type of error:
| Error | Retry? | Recommendation |
|---|---|---|
429 rate_limited | Yes | Wait the seconds in the Retry-After header before retrying, not immediately. If it persists, lower your call rate, see Rate limits and app status |
500 internal, network errors, timeouts | Yes | Retry with exponential backoff, such as waiting 1, 2 and 4 seconds with random jitter, and alert if it still fails after a few tries. For retrying writes, see below |
401 unauthenticated | Once, after getting a new token | Get a new App Token and retry once; stop if it fails again, or if details.reason is app_deleted or tenant_closed |
409 version_conflict | Decide after reading | Read the latest data, confirm the change is still needed, and resubmit with the new version |
410 file_expired, 403 file_blocked | No | The file expired, was deleted or was blocked, so retrying won't succeed. Clients show "file expired" or "file blocked" |
403 tenant_unavailable, 403 app_unavailable | No | The service is suspended or read-only, so retrying soon is pointless. Raise an alert and check the tenant and app status in the console; queries still work in read-only state. When details.reason is arrears, pay the overdue amount, see Overdue invoices and arrears |
403 message_rejected, 403 content_rejected, and 403 permission_denied with details.reason app_rejected | No | The content failed moderation or was rejected by your sync callback; retrying the same content won't succeed, so prompt the user to change it. When details.reason is check_timeout (third-party moderation timed out) or callback_unavailable (sync callback failed), you can retry later |
409 limit_exceeded | No | A count limit was reached and retrying won't succeed. When app-level quotas such as registered users, total groups, storage capacity or chat rooms are full, delete data you no longer need or change plans, see Plan quotas |
Other 4xx | No | The request itself is wrong (parameters, permissions or data state); fix it before resubmitting |
Retry writes idempotently. After a network timeout you can't tell whether the previous request ran. Queries can simply be retried; handle writes per Retries and idempotency:
- Include a
client_msg_idyou generate when sending messages, and retry with the same value after a timeout, so nothing is sent twice; - When creating returns
409 already_existsor deleting returns404 not_found, treat the earlier request as having succeeded; - For other writes, query the result first, then decide whether to retry.
Log the request_id. Log each failed request's request_id, along with the time, method, path and error code, and provide it when contacting support so the request can be found quickly. You can also pass your own trace ID in the X-Request-ID request header, which the server uses as is, to match your logs with our investigation:
curl "$IM_API/$ORG/$APP/users/zhangsan" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "X-Request-ID: order-svc-7f3a9c"Standalone RTC channels
Channel APIs use two additional common error codes: 409 conflict for resource or idempotency state conflicts, and 503 unavailable for temporary unavailability. They don't replace already_exists and version_conflict. Values of details.reason and what to do:
code | details.reason | What to do |
|---|---|---|
invalid_argument | invalid_channel_id, invalid_request, invalid_role | Fix the parameters |
not_found | channel_not_found, session_not_found | The channel or session doesn't exist, or you aren't allowed to see it. Also returned for invalid tickets, see Client ticket errors |
permission_denied | channel_banned, rtc_disabled, rtc_suspended | Banned, or the feature isn't on; fix it before trying again, and don't rejoin automatically |
conflict | channel_closed, session_left, join_deadline_exceeded | Stop this session and clean up |
conflict | media_resource_busy, joined_on_other_device, session_already_exists | Get the current session: leave the old one, or keep using it |
conflict | idempotency_conflict | The same join or operation ID was used with different parameters; it can't be reused that way |
already_exists | channel_id_used | Use another channel ID; closed channel IDs can't be reused |
version_conflict | version_conflict, members_changed | Read the version again; fetch the member list again from the first page |
limit_exceeded | channel_full, active_media_limit, channel_limit | Wait for seats or quota to free up, or adjust capacity and quotas |
unavailable | media_preparing, media_reclaiming, media_unavailable, admission_unavailable | Retry a limited number of times per Retry-After, keeping the original join ID |
unavailable | deployment_config_changed | The server instance's configuration needs syncing; contact the platform. Switching to a new join ID won't get around it |
- A
503when joining may carry yoursession_idandjoin_deadline; retries don't extend the first-join deadline. - Don't retry failed ticket issuing automatically, see Issue tickets.
- After a media token expires or the HTTP APIs deny authorization, you can't keep taking part even if the LiveKit connection is still up.
- SDKs report
unsupportedwhen there's no media capability locally; for capture restrictions and permission errors, see each platform's channel guide.
Optional message metadata
Error objects may carry two optional fields: error.message_key, a stable message key, and error.message_params, parameters to fill into the message, which you can use to show errors in your own wording.
- Not every error has them; use
messagewhen they're absent; - Don't judge whether a request succeeded by whether
messageis in Chinese; code,message,detailsandrequest_idwork as usual; rejection prompts from your sync callbacks are returned as given, without translation.
