Erros, logs e eventos
Erros, logs e eventos são como um programa Solana responde quando estado sozinho não basta: falhe com código, deixe trilha humana ou publique registro estruturado para indexadores. Atribua a cada canal um papel e suporte encurta, CU permanece previsível e dApps param de mostrar "Transaction failed."
Noções básicas de tratamento de erros é a entrada prática; Enums de erro customizados, Logging com msg!, Emitindo eventos, Decodificando erros off-chain e Depurando transações falhas aprofundam cada mecanismo. Esta página é o mapa por baixo: como as peças compõem do handler ao toast da carteira.
Resumo
- Programas comunicam resultados por três canais com contratos diferentes: erros falham a instrução (e transação) com código legível por máquina, logs escrevem linhas legíveis em meta de transação e eventos serializam payloads tipados em logs para consumidores off-chain confiáveis.
- Por que importa: Usuários e bots nunca veem seus stack traces Rust. Eles veem resultados de simulação,
logMessagesdo explorer e o que seu cliente mapeia deProgramError::Custom. Projete essas superfícies de propósito ou você entrega falhas opacas e desperdício de CU. - Conceitos-chave: ProgramResult / ProgramError, enums de erro customizados (u32 estável), custo compute de msg!, sol_log_data / Anchor emit!, tabelas de erro IDL, debug com simulação primeiro, logs públicos (sem segredos).
- Quando usar: Projetar modos de falha para vault ou market, instrumentar hot path, conectar clientes @solana/kit ou Anchor para decodificar falhas, triar incidentes mainnet/devnet ou explicar observabilidade de programa para equipe frontend.
- Limitações / trade-offs: Todo log e evento gasta compute units; todo código customizado é API pública que você deve versionar; instruções falhas revertem estado (e emissão de evento nesse caminho); logs não são mecanismo de autorização.
- Tópicos relacionados: noções básicas de tratamento de erros, enums de erro customizados, logging com msg!, emitindo eventos, decodificando erros off-chain, depurando transações falhas.
Fundamentos
Um handler de instrução Solana é essencialmente uma função que retorna ProgramResult (Result<(), ProgramError>). Sucesso confirma mutações de conta para aquela transação; falha aborta tudo e reverte essas mutações. Não há commit parcial de "campos de erro" como alguns servidores de app escrevem linha de erro e ainda assim sucedem. A falha em si é o sinal.
Esse design empurra observabilidade para metadados em torno da execução, não em contas duráveis "last_error" (a menos que você construa tal estado em caminhos de sucesso). Três canais complementares preenchem a lacuna:
| Canal | Consumidor principal | Semântica de falha | Payload típico |
|---|---|---|---|
| Erro | Runtime, carteiras, decoders dApp | Instrução falha; estado reverte | ProgramError built-in ou Custom(u32) |
Log (msg!) | Engenheiros, explorers, suporte | Visível para passos executados até falha | Linha(s) em texto livre |
| Evento | Indexadores, analytics, webhooks | Emitido em caminhos que sucedem o suficiente para logar; ix falha não deixa estado de evento durável | Struct Borsh/IDL em logs |
Handler de instrução
|
+-- checar invariantes ---- Err(código Custom) ----> falha tx + logs até aqui
|
+-- msg!("step=...") ---- custo CU -------------> logMessages
|
+-- emit!(Event {..}) --- bytes estruturados ----> indexadores / consumidores IDL
|
+-- Ok(()) -------------- confirma escritas de contaErros são o contrato de fluxo de controle. Prefira códigos customizados tipados para regras voltadas ao usuário (InsufficientFunds, SlippageExceeded) e built-ins para falhas genéricas de argumento ou forma de conta quando realmente se aplicam. Logs são canal de debug e ops: curtos, prefixados, controlados por feature quando possível. Eventos são o canal estruturado para sistemas off-chain que não devem analisar prosa.
Os três compartilham uma propriedade: logs de transação são públicos. Trate mensagens, linhas de log e campos de evento como dados de outdoor. Seeds, chaves privadas, PII e internals prontos para exploit não pertencem lá.
Mecânica e interações
Enums de erro customizados: códigos estáveis, não strings livres
Rust on-chain não retorna erros String úteis ao cliente. Clientes veem program ID mais código de erro (frequentemente hex em explorers: custom program error: 0x1770). Seu trabalho é tornar esse número estável, documentado e decodificável.
Programas nativos tipicamente definem enum #[repr(u32)] e implementam From em ProgramError::Custom. Anchor 0.32.1 usa enums #[error_code] que vão para IDL com nomes e strings opcionais #[msg("...")], convencionalmente com offset a partir de 6000. De qualquer forma:
- Variantes apenas append para códigos existentes nunca remapearem silenciosamente.
- Publique a tabela com versão do programa (IDL JSON ou
errors.jsonmantido à mão para nativos). - Namespace na UI por program ID quando transação tem múltiplos programas, pois o mesmo
u32pode significar coisas diferentes em programas diferentes.
Detalhes e padrões em Enums de erro customizados e Noções básicas de tratamento de erros. O ponto do explainer: o enum é superfície de API pública, tão importante quanto discriminators de instrução.
msg! custa: orchestre logs como código em hot path
solana_program::msg! (e wrappers Anchor) invocam syscall de log. Custo escala com chamadas e tamanho do payload. Em orçamento CU apertado, loop de logs formatados pode levar transação a compute excedido e parecer misterioso até você ler a simulação.
Regras práticas:
- Prefira uma linha curta por decisão em vez de dumps multi-linha de bytes de conta.
- Controle logging verboso com features Cargo ou cfg para builds mainnet quietos.
- Use prefixos estáveis (
VAULT:deposit_ok) para ops fazerem grep sem arqueologia. - Nunca logue segredos (seeds, metadados privados ou payloads que não armazenaria publicamente).
Logging brilha em validadores locais, traces Surfpool, testes LiteSVM e instrumentação curta de incidente. É substituto fraco a longo prazo para erros tipados (UX) ou eventos (indexadores). Veja Logging com msg!.
Eventos estruturados: sucesso legível por máquina (e progresso)
Quando dashboards, motores de risco ou plugins Geyser precisam de campos confiáveis, parsing de msg! em texto livre vira passivo. Anchor emit! / emit_cpi! e convenções nativas sol_log_data colocam structs tipados no fluxo de log com schema definido por IDL ou docs.
Eventos ainda custam CU e permanecem públicos. Mantenha payloads pequenos (pubkeys, amounts, enums), versione layouts quando campos quebram e trate estado de conta como autoritativo. Eventos descrevem resultados para consumidores; não são segundo ledger. Instruções falhas não confirmam estado de programa, então não confie em "eventos de erro" como verdade durável. Decodifique códigos de erro para UX de falha; use eventos para resultados de domínio bem-sucedidos. Mergulho: Emitindo eventos.
Decodificação off-chain: de Custom(n) para linguagem de produto
Carteiras frequentemente mostram falha genérica. Seu dApp (ou ferramenta de suporte) deve fazer melhor:
- Execute
simulateTransaction(ou confirme e busque meta) com mesmas contas e dados. - Extraia program ID falhando e código customizado (decimal ou hex).
- Mapeie por erros IDL (Anchor) ou tabela publicada (nativo) via helpers gerados para @solana/kit 7.0.0 ou lookup próprio.
- Mostre mensagem de produto, string i18n opcional e dica de retry que depende do código (slippage vs fundos insuficientes vs não autorizado).
Fixe artefato decoder ao hash do programa implantado. IDLs obsoletas são causa comum de ruído de suporte "nome de erro errado". Padrões client completos: Decodificando erros off-chain.
Depurando transações falhas: logs até o corte
Quando transação executa e falha, meta tipicamente inclui logMessages para trabalho realizado até o erro. Use essa stack para ver qual programa foi invocado, qual CPI estava ativo e qual código customizado encerrou a execução. Quando transação nunca pousa (blockhash, drop, taxa insuficiente), você tem classe de problema diferente: sem logs de programa porque programa nunca rodou.
Loop confiável:
Relato usuário / falha CI
|
v
Simule mesma mensagem --> leia logs + err
|
v
Mapeie código via IDL --> forme hipótese
|
v
Reproduza no LiteSVM / Surfpool --> adicione teste de regressão
|
v
Corrija (código, contas, CU ou cliente) + remova logs temporáriosCLI (solana confirm -v, APIs sim), explorers, traces Surfpool 0.12.0 e testes unitários LiteSVM consomem os mesmos sinais fundamentais: códigos e logs. Detalhes de fluxo: Depurando transações falhas.
Mapa de observabilidade para programas
Use um mapa compartilhado entre equipes de programa e cliente:
| Pergunta | Canal | Ferramenta on-chain | Ferramenta off-chain |
|---|---|---|---|
| Esta ix deve falhar? | Erro | Err(MyError::..) / require! | Decodifique código; bloqueie envio em falha de sim |
| Qual passo rodou? | Log | msg! / vlog controlado por feature | Explorer / sim logMessages |
| Qual fato de negócio indexadores devem armazenar? | Evento | emit! / sol_log_data | Plugin Geyser, indexador customizado |
| Estado está correto após sucesso? | Dados de conta | Escritas sob regras de owner | RPC getAccount / subscriptions |
Erros decidem fluxo de controle. Logs explicam engenheiros. Eventos alimentam máquinas. Estado de conta resolve disputas. Misturar esses papéis (por exemplo analisar logs em prosa para contabilidade crítica) cria sistemas frágeis.
Considerações avançadas e aplicações
Limites CPI complicam o quadro. Quando seu programa invoca outro, erro do callee pode aparecer nos logs externos. Mapeie com cuidado: repropague, encapsule em seu próprio código UX ou deixe código do callee para explorers multi-programa honestos. Sempre decodifique com (program_id, código), não código sozinho.
Segurança e UX puxam em direções opostas. Erros granulares ajudam usuários a corrigir inputs; erros excessivamente granulares podem ensinar atacantes qual checagem falhou. Prefira categorias estáveis em vez de mensagens de produção "debug dump". Coloque detalhe de caça a exploit atrás de logs não-mainnet controlados, não strings #[msg] permanentes.
Orçamento de compute importa em mercados e rotas multi-hop. Falhas que aparecem só sob carga por spam de log são classe real de incidente. Perfile CU em simulação após upgrades (este site fixa Agave 4.1.1) e trate remoção de log como mudança legítima de performance.
Versionamento liga binário de programa, IDL e tabela de erros. Programas raramente atualizados congelam API de erro; planeje códigos cedo. Programas atualizáveis devem arquivar artefatos decoder por deploy para txs históricas permanecerem explicáveis.
Testes devem assertar códigos, não só sucesso. Testes LiteSVM e Anchor que esperam Custom(6001) capturam renumeração silenciosa. Testes de cliente devem decodificar erros de sim fixture da mesma forma que produção.
| Abordagem | Força | Fraqueza | Melhor encaixe |
|---|---|---|---|
Apenas ProgramError built-in | Simples, portátil | UX fraca de produto | Ferramentas internas, protótipos iniciais |
| Enums customizados + IDL | UX estável, pronta para i18n | Deve versionar com cuidado | Protocolos voltados ao usuário |
msg! pesado em todo lugar | Debug local rápido | CU, ruído, dados públicos | Builds dev, incidentes curtos |
| Eventos estruturados | Schema amigável a indexador | CU + manutenção de schema | Analytics, telemetria de protocolo |
| Sim + decode no dApp | Melhor mensagem ao usuário | Precisa tabelas mantidas | Carteiras, UIs de trading, ferramentas de suporte |
Equívocos comuns
- "Vou retornar erro string e a carteira mostrará." Clientes veem principalmente códigos e linhas de log; projete códigos
u32estáveis e decodifique você mesmo. - "Logs são debug grátis." Cada
msg!gasta compute units e pode causar falhas em orçamentos apertados. - "Eventos são armazenamento durável." São payloads de log estruturados para consumidores; estado de conta é fonte da verdade e instruções falhas não confirmam estado.
- "Posso logar seeds e chaves admin; só minha equipe lê logs." Logs de transação são dados públicos da chain visíveis em explorers e arquivos.
- "Erro customizado 6001 sempre significa a mesma coisa na Solana." Códigos são por programa; sempre combine com program ID.
- "Se explorer não mostra logs, meu programa deu panic sem código." Muitas "falhas" nunca executam (expiração, drop, taxa). Separe problemas de landing de erros de programa.
- "Reordenar variantes de enum é refactor puro." Renumera códigos e quebra todo mapa de cliente; apenas append.
FAQs
Qual a diferença entre erro, log e evento?
Erro falha a instrução com código e reverte estado. Log é linha em texto livre em meta de transação para humanos. Evento é payload tipado escrito em logs para indexadores decodificarem campos com confiança.
Por que handlers de instrução retornam ProgramResult?
Para o runtime confirmar em Ok(()) ou abortar a transação inteira em Err(ProgramError), mantendo atomicidade multi-instrução.
Quando usar ProgramError::Custom em vez de built-ins?
Quando a falha é específica do domínio e clientes precisam de código estável e nomeado para UX ou automação. Use built-ins para problemas genéricos de argumento ou conta que realmente correspondam a essas semânticas.
Como Anchor atribui códigos de erro?
Enums #[error_code] são exportados na IDL com códigos numéricos convencionalmente a partir de 6000, mais strings de mensagem opcionais para ferramentas.
Por que msg! custa compute units?
Logging é syscall do runtime; mais chamadas e strings maiores consomem mais do orçamento de compute da transação, o que pode fazer a instrução falhar se o orçamento for apertado.
Programas em produção devem manter msg! verboso?
Geralmente não. Prefira logs verbosos controlados por feature, breadcrumbs permanentes curtos apenas onde ops tem alto valor e eventos estruturados para necessidades de indexador.
Eventos são visíveis se a transação falhar?
Instruções falhas não confirmam estado de programa. Projete UX de falha em torno de códigos de erro e logs capturados até o ponto de falha, não em torno de contas "evento de erro" duráveis, a menos que construa isso deliberadamente em caminho de sucesso.
Como clientes @solana/kit decodificam erros?
Combinam código customizado de simulação ou meta confirmada com IDL ou tabelas de erro geradas, idealmente fixadas à versão do programa com que o app conversa.
Qual o primeiro passo quando usuário diz que transação falhou?
Determine se pousou e executou. Se sim, puxe logs e código de erro (ou simule a mesma mensagem). Se nunca executou, depure taxas, blockhash e caminho de landing primeiro.
Como decodificar transações multi-programa?
Percorra stack de invoke nos logs, identifique qual program ID falhou, depois mapeie tabela de códigos desse programa. Nunca assuma que inteiro nu é seu.
Panics podem substituir erros tipados?
Não em caminhos controlados pelo usuário. Panics abortam sem API de erro de produto limpa e são mais difíceis de mapear em clientes. Retorne Err com código deliberado.
Eventos substituem subscriptions de conta para indexadores?
Complementam. Eventos dão fatos de domínio explícitos; subscriptions de conta dão diffs de estado. Muitos pipelines em produção usam ambos.
Como manter tabelas de erro em sync com deploys?
Publique IDL ou errors.json no CI junto ao artefato de programa, versione com hash de deploy e recuse releases de cliente que fixem tabela errada.
Onde fica i18n de erros?
Off-chain. Mapeie códigos estáveis para strings de locale no cliente; não confie apenas em strings #[msg] em inglês on-chain como único texto ao usuário.
Como esse mapa muda para stacks Pinocchio ou só nativas?
Os canais do runtime são os mesmos: ProgramError, syscalls de log e dados de log binário opcional. Você possui mais da história de publicação IDL/tabela sem macros Anchor.
Relacionados
- Noções básicas de tratamento de erros - ProgramResult e retorno de falhas
- Enums de erro customizados - Códigos de erro estáveis e amigáveis à IDL
- Logging com msg! - Logs humanos conscientes de CU
- Emitindo eventos - Eventos estruturados para indexadores
- Decodificando erros off-chain - Mapear códigos em clientes
- Depurando transações falhas - Simulação e triagem de logs
Versões da stack: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1 e @solana/kit 7.0.0.