{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-parceiros/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Lidar com erros","description":"APIs públicas da Movvia para parceiros, estabelecimentos comerciais e clientes de dados veiculares.","meta":[{"name":"theme-color","content":"#7E3DEE"},{"name":"apple-mobile-web-app-title","content":"Movvia Docs"},{"name":"application-name","content":"Movvia Docs"}],"llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"lidar-com-erros","__idx":0},"children":["Lidar com erros"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"estratégia-geral","__idx":1},"children":["Estratégia geral"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Classifique o erro antes de decidir a ação:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Classe"},"children":["Classe"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Status"},"children":["Status"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Ação"},"children":["Ação"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Erro do cliente"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["4xx (exceto 429)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Corrigir a requisição; não retentar sem mudança"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rate limit"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["429"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Aguardar reset e retentar"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Erro de servidor"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5xx"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Retentar com backoff exponencial"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Conflito de estado"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["409"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verificar estado atual antes de retentar"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"tratar-4xx","__idx":2},"children":["Tratar 4xx"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Erros 4xx indicam problema na sua requisição. Não faça retry sem corrigir a causa."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"async function chamarApi(url: string, options: RequestInit) {\n  const res = await fetch(url, options);\n\n  if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n    const erro = await res.json();\n    // Logar código de erro para diagnóstico\n    console.error(`Erro ${res.status}: ${erro.erro} — ${erro.mensagem}`);\n    throw new Error(`Erro de cliente: ${erro.erro}`);\n  }\n\n  return res;\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"tratar-429-com-backoff","__idx":3},"children":["Tratar 429 com backoff"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"async function chamarComBackoff(\n  fn: () => Promise<Response>,\n  maxTentativas = 4\n): Promise<Response> {\n  for (let i = 0; i < maxTentativas; i++) {\n    const res = await fn();\n\n    if (res.status !== 429) return res;\n\n    const resetHeader = res.headers.get('X-RateLimit-Reset');\n    const aguardar = resetHeader\n      ? Math.max(Number(resetHeader) * 1000 - Date.now(), 0)\n      : Math.pow(2, i) * 1000;\n\n    console.warn(`Rate limit atingido. Aguardando ${aguardar}ms...`);\n    await new Promise(r => setTimeout(r, aguardar));\n  }\n  throw new Error('Rate limit excedido após todas as tentativas');\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"tratar-5xx-com-retry-exponencial","__idx":4},"children":["Tratar 5xx com retry exponencial"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"async function chamarComRetry(\n  fn: () => Promise<Response>,\n  maxTentativas = 3\n): Promise<Response> {\n  const intervalos = [1000, 5000, 30000];\n\n  for (let i = 0; i < maxTentativas; i++) {\n    try {\n      const res = await fn();\n      if (res.status < 500) return res;\n      console.warn(`Erro ${res.status} na tentativa ${i + 1}`);\n    } catch (e) {\n      console.warn(`Falha de rede na tentativa ${i + 1}:`, e);\n    }\n\n    if (i < maxTentativas - 1) {\n      await new Promise(r => setTimeout(r, intervalos[i]));\n    }\n  }\n  throw new Error('Serviço indisponível após retries');\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"success","name":"Idempotência no retry"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Erros 5xx não consomem a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["idempotencyKey"]},". Reenvie a mesma chave no retry — é seguro e evita duplicatas."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"tratar-409-em-pedidos","__idx":5},"children":["Tratar 409 em pedidos"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Um ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["409 Conflict"]}," em confirmação indica que o pedido já mudou de estado. Verifique o estado atual antes de retentar:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"async function confirmarComVerificacao(pedidoId: string, key: string) {\n  const res = await fetch(`${BASE_URL}/pedidos/${pedidoId}/confirmar`, {\n    method: 'POST',\n    headers: headers,\n    body: JSON.stringify({ idempotencyKey: key }),\n  });\n\n  if (res.status === 409) {\n    // Verificar estado atual\n    const estadoRes = await fetch(`${BASE_URL}/pedidos/${pedidoId}`, { headers });\n    const pedido = await estadoRes.json();\n    console.log(`Pedido já em estado: ${pedido.status}`);\n    // Se já confirmado, operação foi bem-sucedida — não é erro\n    if (pedido.status === 'confirmado') return pedido;\n    throw new Error(`Conflito inesperado: ${pedido.status}`);\n  }\n\n  return res.json();\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"checklist-de-robustez","__idx":6},"children":["Checklist de robustez"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Todos os erros logam o campo ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["erro"]}," (código de aplicação), não só o status HTTP."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Retry implementado com backoff exponencial para 5xx e 429."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["idempotencyKey"]}," reutilizada nos retries de 5xx."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Erros 4xx lançam exceção sem retry (exceto 429)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Conflitos 409 verificam estado atual antes de agir."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," Timeout de conexão configurado (recomendado: 10s)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"próximos-passos","__idx":7},"children":["Próximos passos"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/parceiros/guides/erros"},"children":["Guia de erros"]}," — catálogo completo de códigos"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/parceiros/guides/idempotencia"},"children":["Guia de idempotência"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/parceiros/guides/rate-limits"},"children":["Guia de rate limits"]}]}]}]},"headings":[{"value":"Lidar com erros","id":"lidar-com-erros","depth":1},{"value":"Estratégia geral","id":"estratégia-geral","depth":2},{"value":"Tratar 4xx","id":"tratar-4xx","depth":2},{"value":"Tratar 429 com backoff","id":"tratar-429-com-backoff","depth":2},{"value":"Tratar 5xx com retry exponencial","id":"tratar-5xx-com-retry-exponencial","depth":2},{"value":"Tratar 409 em pedidos","id":"tratar-409-em-pedidos","depth":2},{"value":"Checklist de robustez","id":"checklist-de-robustez","depth":2},{"value":"Próximos passos","id":"próximos-passos","depth":2}],"frontmatter":{"title":"Lidar com erros — Parceiros Movvia","description":"Como tratar 4xx, 5xx e retentativas de forma robusta na API de Parceiros Movvia.","seo":{"title":"Lidar com erros"}},"lastModified":"2026-04-25T15:17:56.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/parceiros/tutorials/lidar-com-erros","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}