Перейти к основному содержимому

Коды ошибок

Актуально для: SimpleTwo 0.9.x · Собрано из объявлений в службе

Отказ выглядит так:

{ "code": "not_a_participant",
"error": "You are not a member of that conversation." }

Ветвиться нужно на code: он машиночитаемый и никогда не переименовывается и не переиспользуется. Поле error — английское предложение и запасной вариант для клиента, который кода не знает; оно видно пользователю дословно, поэтому в своей интеграции показывайте либо свой перевод по коду, либо это предложение как есть.

Общее правило по статусам — в обзоре API: 400 — баг в запросе, 401 — продлить токен и повторить один раз, 403 — это ответ и повторять не нужно, 410 — форма ушла и в сообщении названа замена.

Таблица собрана автоматически

Ниже — перенос каталога из репозитория продукта, где он генерируется из объявлений кодов в коде службы. Значит здесь не может оказаться кода, которого нет в продукте, — и наоборот. Описания на английском: это ровно тот текст, который увидит клиент, не знающий кода.

КодЧто значит
client_too_oldthis build predates the oldest protocol version the service still serves. Nothing the user can do inside the app will help; it must be updated. Sent with 426 Upgrade Required, which is the one status HTTP has for exactly this.
call_step_is_signallinga call STEP was posted as a message. Steps are signalling and go to POST /v1/signal; only the call SUMMARY is chat content. A client that sees this is too old to place calls at all, because steps now arrive as signal events it does not listen for — so the user is told to update rather than told about a shape.
not_a_participantthe caller is not a member of the conversation they addressed. An answer, not a failure: do not retry.
system_bot_cannot_be_blockedthe target of a block is a bot the organization vouches for (Bot.System). Block means "you cannot reach me", and the installation reaches people through these: support, announcements. A block there is a channel an admin still believes in, silently severed at one user, with nothing anywhere reporting it. Mute is the control that fits the want — it is allowed on a system bot like on anyone else.
system_messages_are_the_serversa client tried to post a service message ("X removed Y"). Those are written by the service; the one exception is the call summary, admitted by its shape and carrying no identity.
channel_members_may_only_commenta channel member with comments enabled tried to post into the MAIN lane. Comments-on grants the reply path, not the lane the channel speaks in: "members can reply; otherwise only admins post". The client offers the composer only with a reply armed, and this is the same rule where it is enforced — a client hiding a control is not enforcement.
model_provider_unreachablethe AI provider did not answer. Nothing the caller did is wrong and nothing about the request will fix it: retry later, and if it persists the provider or its credentials are the thing to look at.
unknown_kindthe message or stanza named a kind this service does not implement. A client bug rather than a state: reporting it is more useful than retrying it.
malformed_bodythe request body or a required parameter did not parse. A client bug: the same request will fail the same way, so retrying it is not the answer.
services_onlythis door is for another SERVICE of the deployment (a token minted by a service for itself), and a person or a bot knocked. Nothing a user does produces it; if an app shows it, the app is calling a service-to-service endpoint and that is the bug.
skill_level_invalida skill level outside 1..5. The range is rejected rather than clamped on purpose: a 7 quietly stored as 5 would overstate the person on every card and in every criterion that counts "level 4 and above".
unknown_skillthe skill is not in this organisation's catalogue. Pick one from the list, or propose it: free-text skills are moderated so the vocabulary does not fork into three spellings of the same thing.
skill_not_declaredthat person has not claimed this skill (or you have not, when withdrawing your own). Nothing to endorse or remove; not a failure, an answer.
kudos_budget_spentthis month's giving budget is used up. A typed refusal rather than a silent drop, because a thanks that vanished is worse than one that was refused: the giver believes it arrived. The budget is the scarcity that makes recognition mean something (ADR-0037), so it is enforced at write and never "mostly".
kudos_selfthanking yourself. Blocked at the credential, like self-endorsement: a bot or a delegated session acting for you is still you.
kudos_tag_unknowna value tag outside the tenant's set. Rejected rather than stored, because a rank criterion that counts tags would otherwise count typos.
endorsement_above_claiman endorsement above the level its owner claims. A confirmation cannot invent a level the person never declared; endorse the level they claim, or ask them to raise it first.
not_qualified_to_endorseyour own confirmed level in this skill is below the level you are confirming. This is what keeps an endorsement worth having: it is the difference between a colleague vouching for work they can judge and a click from anybody.
self_endorsementnobody confirms their own skill, however qualified they are. The check is on the signed-in account, so acting through a bot or a delegate does not change the answer.
recognition_disabledthis organisation has not switched skills and recognition on. Not a failure and not a permission: the feature is absent here, so a client hides its surfaces rather than offering a retry.
recognition_unavailablethe skills store did not answer. Nothing about the request is wrong: try again shortly, and if it persists the service is the thing to look at.
calls_not_configured── calls (ADR-0005, ADR-0029 addendum) ────────────────────────────────────────────── Calls fail in ways that look alike from the outside and are not: a deployment with no SFU, an account with no permission, every SFU busy, a relay that refused. A client that can only show one sentence for all of them teaches people to read "the call did not work" as "this product does not work. CodeCallsNotConfigured — this deployment has no SFU at all. Not a permission and not an outage: nobody here can place a call until an administrator deploys one, so a client hides the call buttons rather than offering a retry that cannot succeed.
calls_not_permittedthe account may not place or join calls (calls:join). Held by a role, so the answer will not change by trying again; ask whoever administers roles.
not_reachablethe caller's account may not START a conversation with that person. A property of the CALLER's role (its directory scope), not of the person addressed, and not a statement that they exist: the same answer is given for somebody outside the caller's scope and somebody who is not there at all, because telling the two apart is a directory this refusal has no business exposing.
call_host_requiredthe action is a host's (calls:host): muting somebody else, removing them, promoting them, starting or stopping a recording. Being in the call is not enough, and knowing the room's id is not a permission.
no_sfu_availablethere are SFU nodes, and none is currently able to take the call. Distinct from CodeCallsNotConfigured on purpose: this one is worth retrying in a minute and worth telling an administrator about if it persists, and the other is neither.
call_action_unknownthe moderation action is not one this service implements. A client bug rather than a state: the same request will be refused the same way.
sfu_request_failedthe SFU itself refused or did not answer. Nothing about the caller's request is wrong; the media server is the thing to look at. Retrying is reasonable once, and a client should not present it as the user's mistake.
call_token_failedthe join credential could not be signed. This is ours, not the caller's: the signing key is missing or malformed, and no client action fixes it.
recording_not_configuredrecording is asked for on a deployment with no object store to write it to. An administrator sets the bucket; until then the control is offered by a client that does not know, and this is how it finds out.
direct_call_is_1to1an end-to-end call was negotiated in a conversation with more than two people. Peer-to-peer does not scale past two, and routing a secret conversation's media through the SFU would break the promise its text makes — so the refusal is the product's answer rather than a degradation (ADR-0029 addendum).
call_room_not_yoursa room was named that is not this conversation's. Rooms are derived from conversations precisely so that naming one cannot become a way to enter a call you were not invited to; name the conversation and let the service derive it.
call_pass_requireda room was named with no conversation, and the caller holds no usable pass into it (ADR-0005's room-pass addendum). One code for three reasons — there is no pass, it expired, or whoever granted it closed the door — because the person's answer is the same for all three: ask to be let in again. Which of the three it was is in /healthz's call_pass_refused, for the operator, who does have three different answers.
call_pass_call_endedthe pass is good, and the call it was granted for is over: the room holds nobody, or it holds a different call now. A room's name is the same for every call ever placed in that conversation, so this is the refusal that stops an old invitation letting its holder into a later conversation's call. Not worth retrying.
call_pass_not_grantablenobody may be let into this call by this caller: the caller is not in the room, cannot be heard in it, is trying to close a door they did not open, or the room is one where a guest makes no sense (a secret conversation's call is between two people; a channel's call has an audience, not guests).
pstn_no_route── the telephone network (ADR-0007 §C) ────────────────────────────────────────────── The dial plan itself is the coordinator's (coordinator/internal/dialplan): it owns the document, evaluates it, and hands the comms plane a pushed snapshot rather than answering a question per call. What comes back from that evaluation is one of the codes below, and this file is where they are DECLARED, because this is the catalogue scripts/check-error-codes-are-translated.sh compares the clients against. They are declared before the handler that sends them, and that is deliberate rather than untidy. The alternative is that the wording arrives in the same rushed change as the routing, on a lane where the visible failure is a Russian-speaking user reading English and nothing anywhere reporting it. CodePSTNNoRoute — no dial rule will carry this number for this caller: none matched, or the ones that did are locked to a site the account does not have. It is an answer, not an outage — the same number will not work on a retry. An administrator fixes it in the console's numbering and routing tables, and the usual cause is an account with no office set.
pstn_number_unrecognisedwhat was typed is not a number that can be dialled from this account's office. Check the digits; a number in another country needs its full international form.
pstn_emergency_refusedan emergency number was dialled, and this product does not carry emergency calls. Use a mobile telephone. The refusal is deliberate and is stated before the call is placed as well as after: an emergency call is only useful if it reaches the service for the place the caller physically is, and we cannot establish that (ADR-0007 §C.5).
pstn_country_not_enabledoutbound telephony is not switched on for the country this account's office is in. A deployment decision per jurisdiction, not a per-user permission (ADR-0007 decision I.1); an administrator answers it once for the country.
pstn_destination_not_allowedthe organisation does not allow calls to that country. An allowlist rather than a block on this one number, and it is a spend control: ask an administrator to add the country.
pstn_outside_hoursinternational calling is restricted outside the organisation's working hours. Try in working hours, or ask an administrator for round-the-clock dialling.
pstn_outbound_haltedoutbound telephony is switched off for the whole organisation right now. Somebody pulled the switch on purpose, usually while looking at a bill; it is not a fault with this call and retrying will not help.
unknown_resourcethe sub stanza named a resource outside the closed set, or one whose id is missing (ADR-0040). Named rather than generic because a closed set is only an improvement over free-form strings if being outside it says which name was wrong; a silent subscription to nothing is the failure this whole design rejects.
cannot_subscribethis account may not watch that resource. The SAME answer for "not allowed" and "no producer for this kind yet", deliberately: a client cannot tell the two apart and neither reading gives it anything it may act on.
bad_sub_stepa sub stanza whose step is neither subscribe nor unsubscribe.
internal_errorthe service hit a fault it did not expect (a recovered panic). Nothing about the request is wrong and nothing the user can change will help; retry in a moment, and if it keeps happening it is ours to fix. Named so a client can say that in the reader's language instead of showing "internal error" in English at the one moment the product is already letting them down.
storage_failed── the rest of /v1 ────────────────────────────────────────────────────────────── The 277 refusals that predated codes, converted in one design pass rather than one code per sentence. The rule that kept the set small: a 4xx gets a code per REASON a client can act on or must translate (the wording differs), and a 5xx gets a code per OUTCOME CLASS — the client cannot do anything different for "could not pin" and "could not ban", and the cause is in the log, not on the phone. Where a reason already had a code (malformed_body, unknown_kind, not_reachable) it is reused. CodeStorageFailed — the store did not answer, or a write did not land. Ours, not the caller's: nothing about the request will fix it. Try again in a moment; if it persists, the service is the thing to look at, and the log has the cause.
upstream_failedan external system this service relays to (the AI provider) did not answer, or answered in a shape it could not read. Nothing about the request is wrong; retry once, and if it persists the upstream is the thing to look at.
invalid_tokenthe credential is missing, malformed, expired, or not the kind this route takes (a session where an AI-lane token is needed). Sign in again; a bot needs a new token from its owner.
session_revokedthis session was signed out: from another device, or by an administrator. Distinct from an expired token because the reader should be told that, not "sign in again" as if nothing happened. Sign in again.
not_renewablethe session cannot be extended here: it was revoked, it has expired, or it was minted without a renewal grant. One answer for all of them on purpose (which applies is not something an unauthenticated caller is told); the client's move is the same either way — sign in through the coordinator.
insufficient_rolethe action needs a role in the conversation the caller does not hold: owner, co-owner, admin, or the author of the message. Includes the rank rules (nobody removes, bans or demotes somebody of equal or higher role). Not a failure: ask a person who holds the role.
bot_scope_deniedthe credential is a bot's, and this action is outside what it may do: a scope it does not hold, a conversation outside its fence, a route a fenced or worker bot has no business on, an action only the owner in person may take. Held by the bot's configuration, so retrying will not change the answer.
bot_disabledthe bot is suspended. One switch stops the whole agent — it neither talks nor thinks — and only its owner or an administrator turns it back on.
delegation_disabledacting on behalf of a person is switched off for this organisation, so every assistant is refused at the door. A tenant decision, not a per-bot fault; an administrator answers it once.
blockeda block stands between the caller and that person, in either direction. An answer, not a failure: nothing to retry.
bannedthe person is banned from the conversation: the caller trying to join, or somebody the caller is trying to add. An admin lifts it; until then it holds.
member_limit_reachedthe conversation is full. The limit is the organisation's setting; ask an administrator to raise it, or remove somebody first.
not_joinablethe conversation does not admit joining this way: it is invite-only, or its link has been revoked. Ask a member for an invitation.
forbiddenthe caller's kind of account may not call this route at all: a coordinator-only route called by a person, a stream opened from an origin the deployment does not serve. Not a conversation role (insufficient_role) and not a bot's fence (bot_scope_denied): a client meeting this is calling something it was never meant to.
photo_managed_by_directorythe profile photo comes from the organisation's directory and cannot be changed or removed in the app. Change it where the directory is administered.
federation_refuseda federated peer was turned away: its domain is not on the allowlist, or its signature did not verify. Server-to-server; a person only meets this through a bug.
federation_disabledfederation is not switched on for this deployment, so the federation routes do not exist here. Server-to-server, like federation_refused.
not_foundthe thing named in the path does not exist here, or the caller may not know whether it does: an attachment, an avatar, a bot, an agent, a person. For a conversation, a message or a thread there are specific codes, because a client can act on those (drop the stale entry from its cache).
conversation_not_foundno such conversation, or none the caller may see. A client holding it in a local cache should drop it.
message_not_foundno message at that seq in that conversation. A client holding it should drop it.
unit_not_foundno such thread or topic in that conversation. A client holding it should drop it, and a message addressed to it is refused rather than filed where nobody is looking.
invite_invalidthe invite link is wrong, revoked or expired. An answer, not a failure: ask for a new link.
not_applicablethe request is well-formed and the action does not apply to this target: members added to a 1:1, an invite link for a direct chat, a topic in a conversation that is not a forum, an icon on a thread, a bot as owner, a reaction outside the conversation's set. A client offering the control is out of date; the server is not hiding anything.
owner_cannot_leavethe owner tried to leave. Transfer ownership first, or delete the conversation.
title_requireda topic needs a title: it IS the topic's name, and it cannot be renamed to nothing.
title_too_longthe title is over the limit (80 characters). Shorten it.
icon_not_emojian icon must be a single emoji. Pick one.
invalid_attachmentthe attachment id does not name an upload the caller may send here: unknown, expired, or in a conversation the caller is not a member of. The same answer for all of them, deliberately. Attach the file again.
invalid_imagethe upload is not an image this service can read (JPEG, PNG or GIF). Choose another picture.
method_not_allowedthe route exists and does not take this HTTP method. A client bug: the same request will fail the same way.
already_memberthat person is already in the conversation. Not a failure.
message_deletedthe message was deleted and cannot be edited. Not a failure.
unit_closedthe thread or topic is closed and takes no more messages. An admin reopens it.
topic_existsa topic with that name already exists in the conversation. Choose another name, or use the one that is there.
too_largethe upload is over the size limit. The sentence states the limit and, when known, the size. Choose a smaller file.
rate_limitedtoo many attempts in a short time (failed sign-ins). Wait a minute and try again.
ai_budget_spentthe bot's daily AI token budget is used up. It resets at midnight UTC; its owner can raise it. Sent on the AI lane, where the reader is a bot program.
not_supportedthe request asks for something this service does not do: streaming on the AI lane, embeddings through a provider that has none, a request the configured provider's dialect cannot express. Not a fault and not a permission.
uploads_disabledthis deployment has no object store, so attachments and photos cannot be uploaded or served. An administrator configures one; a client hides the controls rather than offering a retry that cannot succeed.
ai_lane_not_configuredthe AI lane has no provider key for this bot: neither its own nor the organisation's. Its owner or an administrator sets one.
control_unauthorized── the control plane ───────────────────────────────────────────────────────────── /control/* is called by the coordinator's Go code and, through it, by the admin console — never by a phone. So the reader of these refusals is a program that logs the sentence, or an administrator who reads it in a console card, and there is nothing for a client to translate or branch on per cause: a bad body from the coordinator is a bug in the coordinator, not a state the console offers a button for. That is why the set is FIVE codes by outcome class rather than one per cause, and why the cause stays in the English sentence, which the coordinator surfaces verbatim. Minting a client-grade code for "member roles are member or admin" would put a row in every client's string table for a sentence no client will ever display. But the SHAPE is the same error as everything else, on purpose. One error shape per service is what lets the coordinator's proxy, the console's fetch wrapper and this repo's own checks treat every refusal alike; a plain-text 400 from one route was the exception each of them had to special-case (and one of them did not). CodeControlUnauthorized — the control token is missing or wrong. The listener is guarded by MSG_CONTROL_TOKEN and the coordinator holds the pair; when they disagree every control route fails this way, and the fix is the configuration, not the call.
control_bad_requestthe coordinator sent something this service refuses: a body that does not parse or carries a field it does not know, a missing required value, a rule that fails validation, a cap that would be exceeded. The sentence says which. Retrying the same request will fail the same way.
control_not_foundthe bot, automation or conversation named in the path does not exist here. Usually a coordinator that remembers a thing this service has since lost, or a name typed into a console field; either way, an answer rather than a fault.
control_unavailablethis service cannot serve the request RIGHT NOW: the store did not answer, or a dependency is not configured (the AI lane without a key). Worth retrying in a minute; worth an administrator's attention if it persists.
control_faileda write or a read that should have worked did not (5xx). Ours, not the caller's: nothing about the request will fix it, and the log has the cause.
sealed_no_keythis installation's message bodies are encrypted (ADR-0039) and this service does not currently hold the key, so a new message cannot be written the way the existing ones were. Nothing about the request is wrong and nothing about it will help: an administrator has to give the service its key back. The alternative was storing the message in the clear, which is the one outcome nobody would notice — plaintext rows interleaved with sealed ones in a database whose owner believes it is encrypted. A refusal a sender sees in a second is the only version of this that gets fixed.