Pular para o conteúdo

Erros

Toda recusa tem a mesma forma, e o campo que a sua máquina lê é o "code": 59 códigos de vocabulário fechado, cada um com um status HTTP fixo — e 7 deles nunca chegam a um App.

Como eles chegam até você

Toda recusa vira um TrivoError, e o campo que o seu código lê é o code.

import { TrivoError } from "@trivo/sdk";

async function apagar() {
  try {
    await app.rest.deleteMessage({ serverId, channelId, messageId });
  } catch (erro) {
    if (!(erro instanceof TrivoError)) throw erro;
    if (erro.is("MISSING_PERMISSION", "NOT_MESSAGE_AUTHOR")) return;
    // `code` nulo quer dizer que não foi o Trivo que respondeu — um proxy, o
    // seu túnel, um WAF. Aí o que existe é o `status`.
    if (erro.code === null) console.error(`resposta de fora: ${erro.status}`);
    throw erro;
  }
}

O 429 quase nunca chega até aqui: o SDK espera o Retry-After e repete sozinho, e só devolve o erro quando o prazo passa do teto que você configurou.

Referência de transporte

A forma no fio

O que viaja no fio, para transparência, depuração e compatibilidade — não é um segundo jeito de construir App. Se você está escrevendo um App, o SDK já faz isto.

{
  "code": "MISSING_PERMISSION",
  "message": "Você não tem permissão para isso.",
  "erro": "Você não tem permissão para isso."
}
  • code — vocabulário fechado. É o contrato estável.
  • message — texto em português, para gente ler. Muda quando a copy melhorar, e vai mudar.
  • erro — apelido de message, por compatibilidade com o cliente web. Ele vai sair; não construa nada em cima dele.

Os extras

Conforme o caso, a recusa carrega campos ao lado do código. Todos são opcionais por definição — quem lê o corpo decide pelo code.

CampoQuando aparece
currentRevisionEm STALE_REVISION: a revisão que o servidor tem agora.
retryAfterSecondsEm RATE_LIMIT_EXCEEDED, junto do cabeçalho Retry-After.
resetAtNo mesmo caso, quando o instante da liberação é conhecido.
expiresAtEm SERVER_SUSPENDED: quando a suspensão acaba. Em APP_RESTRICTED: quando a restrição do App acaba (null quando ela não tem prazo).
validIntentsEm UNKNOWN_INTENT: a lista das famílias que existem.
refEm INTERNAL_ERROR: a referência que casa com a linha do nosso log.

O catálogo, por status

Estes são os códigos que uma credencial de App pode receber. Os que não podem estão na seção seguinte, e ficam de fora daqui de propósito: procurar num catálogo de plataforma inteira o que a sua integração jamais verá é tempo perdido em cima da recusa errada.

400Pedido malformado

CódigoO que aconteceu
UNSUPPORTED_VERSIONVocê pediu uma versão da API que o Trivo não serve. A resposta traz a lista do que vale em `validVersions`.
EMPTY_INTENTSO parâmetro `intents` veio presente e vazio. Omita o parâmetro para receber o conjunto padrão.
UNKNOWN_INTENTFamília de intent que não existe. O corpo lista as que valem em `validIntents`.

401Sem credencial

CódigoO que aconteceu
MISSING_CREDENTIALSem credencial válida. Uma resposta só para sessão ausente e para Bearer que não vale.

403Proibido

CódigoO que aconteceu
MISSING_PERMISSIONFalta a permissão para este gesto. Para App, lembre da interseção: cargos ∩ escopos.
ROLE_OUTRANKS_YOUO cargo está acima do seu posto.
MEMBER_OUTRANKS_YOUA pessoa está acima do seu posto. Para um App, o posto é o do cargo mais alto que ele recebeu.
CANNOT_GRANT_PERMISSIONVocê tentou conceder o que não tem.
OWNER_ONLYSó o dono do servidor faz isto.
TARGET_PROTECTEDO alvo não pode receber este gesto.
SERVER_PROTECTEDO servidor não pode receber este gesto.
NOT_MESSAGE_AUTHOREditar ou apagar mensagem de outra autoria.
SERVER_SUSPENDEDO servidor está suspenso. O corpo traz `expiresAt`.
SERVER_BANNEDO servidor está banido.
APP_RESTRICTEDO App está restrito por sanção: ele continua autenticando, conectado e recebendo entrega, e só não pode PÔR COISA NOVA na frente dos outros — publicar e editar mensagem, reagir, criar canal/cargo/categoria e responder interação (reply, modal e sugestões de autocompletar; `defer` passa). Apagar, ajustar o que já existe e registrar comandos continuam. O corpo traz `expiresAt` quando há prazo. Não existe irmão para a restrição da DONA do App: ela não alcança o App em execução.
FEATURE_DISABLEDO recurso está desligado para este escopo.

404Não encontrado

CódigoO que aconteceu
SERVER_NOT_FOUNDServidor inexistente, id torto, ou o App não está instalado nele. Resposta única, de propósito.
CHANNEL_NOT_FOUNDCanal inexistente, de voz, arquivado ou de outro servidor.
MESSAGE_NOT_FOUNDMensagem inexistente ou já apagada.
ROLE_NOT_FOUNDCargo inexistente neste servidor.
CATEGORY_NOT_FOUNDCategoria inexistente neste servidor.
APP_NOT_FOUNDApp inexistente, ou não instalado aqui.
TARGET_NOT_MEMBERA pessoa alvo não é membro deste servidor.
INTERACTION_NOT_FOUNDInteração inexistente, de outro App, ou já varrida.
COMMAND_NOT_FOUNDComando inexistente, de outro App, ou de um App que não está instalado aqui.
COMPONENT_NOT_FOUNDO botão ou a seleção não está naquela mensagem, ou está desligado.
FORM_NOT_FOUNDO formulário não abriu daquela interação, já foi enviado, venceu, ou é de outra pessoa. Uma resposta para os quatro.

409Conflito

CódigoO que aconteceu
STALE_REVISIONAlguém mudou o servidor antes de você. O corpo traz `currentRevision`.
NAME_TAKENEsse nome já está em uso.
MAILBOX_EXPIREDA caixa do gateway não existe mais. Abra outra.
INTERACTION_EXPIREDO prazo da interação passou. O estado é derivado do prazo, não de um temporizador.
INTERACTION_ALREADY_CONFIRMEDOutra execução já confirmou esta interação. É o lock de quem não guarda estado.

422Entrada recusada

CódigoO que aconteceu
INVALID_INPUTO corpo ou o parâmetro não foi entendido, ou foi entendido e não vale.
INVALID_IMAGEA imagem não passou nas regras de formato ou tamanho.
CANNOT_TARGET_SELFEste gesto não vale sobre si mesmo.
BASE_ROLE_IMMUTABLEO cargo base (@everyone) não aceita esta mudança.
APP_ROLE_IMMUTABLEO cargo automático de uma instalação nasce e morre com ela.
SCOPE_NOT_REQUESTEDO App não pediu esse escopo, então ele não pode ser concedido.
ADMIN_CONFIRMATION_REQUIREDConceder `administrador` exige confirmação própria.
NAME_MISMATCHO nome digitado não confere com o do recurso.
TEXT_BLOCKEDO texto foi barrado pela moderação preventiva.
CHANNEL_LIMIT_EXCEEDEDO servidor chegou ao teto de canais.
ROLE_LIMIT_EXCEEDEDO servidor chegou ao teto de cargos.
CATEGORY_LIMIT_EXCEEDEDO servidor chegou ao teto de categorias.
FOLLOWUP_LIMIT_EXCEEDEDEsta interação já teve cinco seguimentos.
COMMAND_LIMIT_EXCEEDEDUm teto de comandos estourou: os do App (globais ou por servidor), ou os visíveis no servidor.
APP_MISSING_SCOPEO comando exige uma permissão que a instalação deste App já não autoriza.
ENDPOINT_REJECTEDO endereço do endpoint não passa nas guardas de saída (https, porta 443, endereço público).
ENDPOINT_UNREACHABLEO endpoint não respondeu 2xx com o desafio ecoado no prazo.

429Ritmo

CódigoO que aconteceu
RATE_LIMIT_EXCEEDEDTeto de ritmo. Espere o que o `Retry-After` disser — é a única decisão possível.

500Falha do lado de cá

CódigoO que aconteceu
INTERNAL_ERRORFalha não prevista do lado do Trivo. O corpo traz `ref`, que casa com a linha do log.

503Indisponível

CódigoO que aconteceu
SERVICE_UNAVAILABLEInfraestrutura conhecida fora do ar.

O que nunca chega a um App

O catálogo é da plataforma inteira, e o back office, a entrada por sessão e a conversa privada leem a mesma lista. Os códigos abaixo saem de molduras que não aceitam Authorization: Bearer — o corte é do núcleo, e não uma leitura desta página. Uma ramificação escrita para qualquer um deles é código morto no seu bot.

CódigoStatusPor que ele não alcança você
ORIGIN_REJECTED403A defesa de origem recusou. Não alcança App: Bearer não é credencial de ambiente.
DM_NOT_FOUND404Conversa privada inexistente. Nenhuma rota de DM aceita credencial de App.
NOT_FOUND404O 404 mudo do back office.
PLATFORM_OWNER_ONLY403Reservado a quem opera a plataforma.
ACCOUNT_RESTRICTED403A conta está restrita por sanção.
ACCOUNT_SUSPENDED403A entrada por sessão foi recusada: a conta está suspensa (`expiresAt` traz o prazo). Não alcança App.
ACCOUNT_BANNED403A entrada por sessão foi recusada: a conta foi banida. Não alcança App.

A pergunta maior — o que um App não alcança de jeito nenhum — tem página própria em O que um App nunca alcança.

O 404 que não distingue

SERVER_NOT_FOUND serve ao servidor inexistente, ao id torto, a quem não é membro, a quem está banido e ao App não instalado. É resposta única de propósito: um cliente que quisesse saber qual dos cinco estaria pedindo exatamente o que a recusa existe para não contar. O mesmo vale para CHANNEL_NOT_FOUND, APP_NOT_FOUND e INTERACTION_NOT_FOUND.

Credencial recusada e gesto recusado

Um App suspenso ou banido não autentica: toda porta responde MISSING_CREDENTIAL, e o gateway fecha. Um App restrito autentica normalmente, fica conectado e continua recebendo interação — o que ele não faz é escrever, e é aí que sai APP_RESTRICTED. Os dois casos ficam separados porque o conserto de quem integra é diferente: no primeiro não adianta tentar de novo com o mesmo token; no segundo o token está bom e o bot deve seguir escutando.

Os dois que parecem iguais e não são

INTERACTION_EXPIRED e INTERACTION_ALREADY_CONFIRMED ficam separados porque exigem conserto diferente de quem integra: o primeiro é "o seu handler está lento"; o segundo é "você recebeu a mesma entrega duas vezes". Fundi-los economizaria um código e custaria a informação que resolve o problema.

Como esta lista cresce

Nenhum código sem produtor: um código declarado "para o futuro" é uma ramificação que o seu SDK escreve e nunca exercita, e um dia alguém a apaga por parecer morta. Código novo entra junto da recusa que o produz.

Renomear ou remover um código existente é quebra de contrato. Dentro de uma versão, nada some. O que pode acontecer é um código novo aparecer — então trate o desconhecido como "falhou, e eu não sei classificar", e não como impossível.