Entender os erros da API

Este guia explica como a API Data Manager processa e comunica erros. Entender a estrutura e o significado dos erros da API é fundamental para criar aplicativos robustos que possam lidar com problemas, desde entradas inválidas até indisponibilidade temporária do serviço.

A API Data Manager segue o modelo de erro padrão da API do Google, que é baseado em códigos de status do gRPC. Cada resposta da API que resulta em um erro inclui um objeto Status com:

  • Um código de erro numérico.
  • Uma mensagem de erro.
  • Detalhes adicionais opcionais do erro.

Códigos de erro canônicos

A API Data Manager usa um conjunto de códigos de erro canônicos definidos por gRPC e HTTP. Esses códigos fornecem uma indicação de alto nível do tipo de erro. Sempre verifique esse código primeiro para entender a natureza fundamental do problema.

Para mais detalhes sobre esses códigos, consulte o Guia de design de API: códigos de erro.

Solucionar erros

Siga estas etapas quando uma solicitação falhar:

  1. Verifique o código de erro para encontrar o tipo de erro.

    • Se você usar o gRPC, o código de erro estará no code campo do Status. Se você usar uma biblioteca de cliente, ela poderá gerar um tipo específico de exceção que corresponde ao código de erro. Por exemplo, a biblioteca de cliente para Java gera uma com.google.api.gax.rpc.InvalidArgumentException se o código de erro for INVALID_ARGUMENT.
    • Se você usar o REST, o código de erro estará na resposta de erro em error.status, e o status HTTP correspondente estará em error.code.
  2. Verifique o payload de detalhes padrão do código de erro. Os payloads de detalhes padrão são um conjunto de mensagens para erros das APIs do Google. Eles fornecem detalhes de erros de maneira estruturada e consistente. Cada erro da API Data Manager pode ter várias mensagens de payload de detalhes padrão. As bibliotecas de cliente da API Data Manager têm métodos auxiliares para receber os payloads de detalhes padrão de um erro.

    Não importa o código de erro, recomendamos que você verifique e registre os ErrorInfo, RequestInfo, Help, e LocalizedMessage payloads.

    • ErrorInfo tem informações que podem não estar em outros payloads.
    • RequestInfo tem o ID da solicitação, que é útil se você precisar entrar em contato com o suporte.
    • Help e LocalizedMessage contêm links e outros detalhes para ajudar você a resolver o erro.

    Além disso, o payload BadRequest é útil para erros INVALID_ARGUMENT, porque fornece informações sobre quais campos causaram o erro.

Payloads de detalhes padrão

Os payloads de detalhes padrão mais comuns para a API Data Manager são:

BadRequest

Verifique o BadRequest payload quando uma solicitação falhar com INVALID_ARGUMENT (código de status HTTP 400).

Uma mensagem BadRequest mostra que a solicitação tinha campos com valores incorretos ou estava faltando um valor para um campo obrigatório. Verifique a lista field_violations no BadRequest para encontrar os campos com erros. Cada entrada field_violations tem informações para ajudar você a corrigir o erro:

field

O local do campo na solicitação, usando uma sintaxe de caminho em camel case.

Se um caminho apontar para um item em uma lista (um campo repeated), o índice será mostrado entre colchetes ([...]) após o nome da lista.

Por exemplo, destinations[0].operating_account.account_id é o account_id na operating_account do primeiro item na lista destinations.

description

Uma explicação de por que o valor causou um erro.

reason

A ErrorReason enumeração, como INVALID_HEX_ENCODING ou INVALID_CURRENCY_CODE.

Exemplos de BadRequest

Confira um exemplo de resposta para um erro INVALID_ARGUMENT com uma mensagem BadRequest. As field_violations mostram que o erro é um accountId que não é um número. O field valor destinations[0].login_account.account_id mostra o accountId com uma violação de campo está no login_account do primeiro item na lista destinations.

{
  "error": {
    "code": 400,
    "message": "There was a problem with the request.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "INVALID_ARGUMENT",
        "domain": "datamanager.googleapis.com",
        "metadata": {
          "requestId": "t-a8896317-069f-4198-afed-182a3872a660"
        }
      },
      {
        "@type": "type.googleapis.com/google.rpc.RequestInfo",
        "requestId": "t-a8896317-069f-4198-afed-182a3872a660"
      },
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "destinations[0].login_account.account_id",
            "description": "String is not a valid number.",
            "reason": "INVALID_NUMBER_FORMAT"
          }
        ]
      }
    ]
  }
}

Confira outro exemplo de resposta de um erro INVALID_ARGUMENT com uma mensagem BadRequest. Nesse caso, a lista field_violations mostra dois erros:

  1. O primeiro event tem um valor que não está codificado em hexadecimal no segundo identificador de usuário do evento.

  2. O segundo event tem um valor que não está codificado em hexadecimal no terceiro identificador de usuário do evento.

{
  "error": {
    "code": 400,
    "message": "There was a problem with the request.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "INVALID_ARGUMENT",
        "domain": "datamanager.googleapis.com",
        "metadata": {
          "requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
        }
      },
      {
        "@type": "type.googleapis.com/google.rpc.RequestInfo",
        "requestId": "t-6bc8fb83-d648-4942-9c49-2604276638d8"
      },
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "events.events[0].user_data.user_identifiers[1]",
            "description": "The HEX encoded value is malformed.",
            "reason": "INVALID_HEX_ENCODING"
          },
          {
            "field": "events.events[1].user_data.user_identifiers[2]",
            "description": "The HEX encoded value is malformed.",
            "reason": "INVALID_HEX_ENCODING"
          }
        ]
      }
    ]
  }
}

RequestInfo

Verifique o RequestInfo payload sempre que uma solicitação falhar. Um RequestInfo contém o request_id que identifica exclusivamente sua solicitação de API.

{
  "@type": "type.googleapis.com/google.rpc.RequestInfo",
  "requestId": "t-4490c640-dc5d-4c28-91c1-04a1cae0f49f"
}

Ao registrar erros ou entrar em contato com o suporte, inclua o ID da solicitação para ajudar no diagnóstico de problemas.

ErrorInfo

Verifique a mensagem ErrorInfo para recuperar informações adicionais que podem não ser capturadas nos outros payloads de detalhes padrão. O payload ErrorInfo contém um mapa metadata com informações sobre o erro.

Por exemplo, confira o ErrorInfo para uma falha PERMISSION_DENIED causada pelo uso de credenciais para um projeto na nuvem do Google Cloud em que a API Data Manager não está ativada. O ErrorInfo fornece mais informações sobre o erro, como:

  • O projeto associado à solicitação, em metadata.consumer.
  • O nome do serviço, em metadata.serviceTitle.
  • O URL em que o serviço pode ser ativado, em metadata.activationUrl.
{
  "error": {
    "code": 403,
    "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "SERVICE_DISABLED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/PROJECT_NUMBER",
          "service": "datamanager.googleapis.com",
          "containerInfo": "PROJECT_NUMBER",
          "serviceTitle": "Data Manager API",
          "activationUrl": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
        }
      },
      ...
    ]
  }
}

Help e LocalizedMessage

Verifique os payloads Help e LocalizedMessage para receber links para documentação e mensagens de erro localizadas que ajudam você a entender e corrigir o erro.

Por exemplo, confira o Help e LocalizedMessage para uma falha PERMISSION_DENIED causada pelo uso de credenciais para um projeto na nuvem do Google Cloud em que a API Data Manager não está ativada. O payload Help mostra o URL em que o serviço pode ser ativado, e o LocalizedMessage tem uma descrição do erro.

{
  "error": {
    "code": 403,
    "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
        "locale": "en-US",
        "message": "Data Manager API has not been used in project PROJECT_NUMBER before or it is disabled. Enable it by visiting https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
      },
      {
        "@type": "type.googleapis.com/google.rpc.Help",
        "links": [
          {
            "description": "Google API Console API activation",
            "url": "https://console.cloud.google.com/apis/api/datamanager.googleapis.com/overview?project=PROJECT_NUMBER"
          }
        ]
      },
      ...
    ]
  }
}

Acessar detalhes do erro

Se você estiver usando uma das bibliotecas de cliente, use os métodos auxiliares para receber os payloads de detalhes padrão.

.NET

try {
    // Send API request
}
catch (Grpc.Core.RpcException rpcException)
{
    Console.WriteLine($"Exception encountered: {rpcException.Message}");
    var statusDetails =
        Google.Api.Gax.Grpc.RpcExceptionExtensions.GetAllStatusDetails(
            rpcException
        );
    foreach (var detail in statusDetails)
    {
        if (detail is Google.Rpc.BadRequest)
        {
            Google.Rpc.BadRequest badRequest = (Google.Rpc.BadRequest)detail;
            foreach (
                BadRequest.Types.FieldViolation? fieldViolation in badRequest.FieldViolations
            )
            {
                // Access attributes such as fieldViolation!.Reason and fieldViolation!.Field
            }
        }
        else if (detail is Google.Rpc.RequestInfo)
        {
            Google.Rpc.RequestInfo requestInfo = (Google.Rpc.RequestInfo)detail;
            string requestId = requestInfo.RequestId;
            // Log the requestId...
        }
        else if (detail is Google.Rpc.ErrorInfo)
        {
            Google.Rpc.ErrorInfo errorInfo = (Google.Rpc.ErrorInfo)detail;
            // Log the errorInfo.Reason and errorInfo.Metadata...

            // Log the details in the 'Metadata' map...
            foreach (
                KeyValuePair<String, String> metadataEntry in errorInfo.Metadata
            )
            {
                // Log the metadataEntry.Key and metadataEntry.Value...
            }
        }
        else
        {
            // ...
        }
    }
}

Java

try {
  // Send API request
} catch (com.google.api.gax.rpc.InvalidArgumentException invalidArgumentException) {
  // Gets the standard BadRequest payload from the exception.
  BadRequest badRequest = invalidArgumentException.getErrorDetails().getBadRequest();
  for (int i = 0; i < badRequest.getFieldViolationsCount(); i++) {
    FieldViolation fieldViolation = badRequest.getFieldViolations(i);
    // Access attributes such as fieldViolation.getField() and fieldViolation.getReason()
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = invalidArgumentException.getErrorDetails().getRequestInfo();
  if (requestInfo != null) {
    String requestId = requestInfo.getRequestId();
    // Log the requestId...
  }
} catch (com.google.api.gax.rpc.ApiException apiException) {
  // Fallback exception handler for other types of ApiException.

  // Gets the standard ErrorInfo payload from the exception.
  ErrorInfo errorInfo = apiException.getErrorDetails().getErrorInfo();
  // Log the 'reason' and 'domain'...

  // Log the details in the 'metadata' map...
  for (Entry<String, String> metadataEntry : errorInfo.getMetadataMap().entrySet()) {
    // Log the metadataEntry key and value...
  }

  // Gets the standard RequestInfo payload from the exception.
  RequestInfo requestInfo = invalidArgumentException.getErrorDetails().getRequestInfo();
  if (requestInfo != null) {
    String requestId = requestInfo.getRequestId();
    // Log the requestId...
  }
  ...
}

Práticas recomendadas para tratamento de erros

Para criar aplicativos resilientes, implemente as práticas recomendadas a seguir.

Inspecionar detalhes do erro
Sempre procure um dos payloads de detalhes padrão, como BadRequest. Cada payload de detalhes padrão contém informações para ajudar você a entender a causa do erro.
Diferenciar erros de cliente e servidor

Determine se o erro é causado por um problema com sua implementação (o cliente) ou um problema com a API (o servidor).

  • Erros do cliente: códigos como INVALID_ARGUMENT, NOT_FOUND, PERMISSION_DENIED, FAILED_PRECONDITION, UNAUTHENTICATED. Eles exigem mudanças na solicitação ou no estado/credenciais do aplicativo. Não repita a solicitação sem resolver o problema.
  • Erros do servidor: códigos como UNAVAILABLE, INTERNAL, DEADLINE_EXCEEDED, UNKNOWN. Eles sugerem um problema temporário com o serviço da API.
Implementar uma estratégia de repetição

Determine se o erro pode ser repetido e use uma estratégia de repetição.

  • Repita apenas para erros de servidor temporários, como UNAVAILABLE, DEADLINE_EXCEEDED, INTERNAL, UNKNOWN e ABORTED.
  • Use um algoritmo de espera exponencial para aguardar períodos cada vez maiores entre as repetições. Isso ajuda a evitar a sobrecarga de um serviço já estressado. Por exemplo, aguarde 1 segundo, depois 2 segundos, depois 4 segundos, continuando até um número máximo de repetições ou tempo total de espera.
  • Adicione uma pequena quantidade aleatória de "jitter" aos atrasos de espera para evitar o problema de "excesso de acionamentos", em que muitos clientes repetem simultaneamente.
Registrar completamente

Registre a resposta de erro completa, incluindo todos os payloads de detalhes padrão, especialmente o ID da solicitação. Essas informações são essenciais para depurar e informar problemas ao suporte do Google, se necessário.

Fornecer feedback do usuário

Com base nos códigos e mensagens nos payloads de detalhes padrão, forneça feedback claro e útil aos usuários do aplicativo. Por exemplo, em vez de apenas "Ocorreu um erro", você pode dizer "O ID da transação estava ausente" ou "O ID da conta do destino não foi encontrado".

Ao seguir estas diretrizes, você pode diagnosticar e lidar com erros retornados pela API Data Manager, resultando em aplicativos mais estáveis e fáceis de usar.