错误

本章简要介绍了 Google API 的错误模型。它还为开发者提供了正确生成和处理错误的通用指南。

Google API 使用独立于协议的简单错误模型,这使我们能够在不同的 API、不同的 API 协议(如 gRPC 或 HTTP)和不同的错误上下文(如异步、批处理或工作流错误)中提供一致的体验。

错误模型

Google API 的错误模型由 google.rpc.Status 逻辑定义,该实例在发生 API 错误时返回给客户端。以下代码段显示了错误模型的总体设计:

package google.rpc;

// The `Status` type defines a logical error model that is suitable for
// different programming environments, including REST APIs and RPC APIs.
message Status {
  // A simple error code that can be easily handled by the client. The
  // actual error code is defined by `google.rpc.Code`.
  int32 code = 1;

  // A developer-facing human-readable error message in English. It should
  // both explain the error and offer an actionable resolution to it.
  string message = 2;

  // Additional error information that the client code can use to handle
  // the error, such as retry info or a help link.
  repeated google.protobuf.Any details = 3;
}

由于大多数 Google API 采用面向资源的 API 设计,因此错误处理遵循相同的设计原则,使用一小组标准错误配合大量资源。例如,服务器没有定义不同类型的“找不到”错误,而是使用一个标准 google.rpc.Code.NOT_FOUND 错误代码并告诉客户端找不到哪个特定资源。错误空间变小降低了文档的复杂性,在客户端库中提供了更好的惯用映射,并降低了客户端的逻辑复杂性,同时不限制是否包含可操作信息。

错误代码

Google API 必须使用 google.rpc.Code 定义的规范错误代码。单个 API 避免定义其他错误代码,因为开发人员不太可能编写用于处理大量错误代码的逻辑。作为参考,每次 API 调用平均处理三个错误代码意味着,大多数应用逻辑只会用于错误处理,这会给开发者带来良好的体验。

错误消息

错误消息应该可以帮助用户轻松快捷地理解和解决 API 错误。通常,在编写错误消息时请考虑以下准则:

  • 不要假定用户是您的 API 的专家用户。用户可以是客户端开发者、运维人员、IT 人员或应用的最终用户。
  • 不要假设用户了解有关服务实现的任何信息,或者熟悉错误的上下文(例如日志分析)。
  • 如果可能,应构建错误消息,以便技术用户(但不一定是 API 开发人员)可以响应错误并改正。
  • 确保错误消息内容简洁。如果需要,请提供一个链接,便于有疑问的读者提问、提供反馈或详细了解错误消息中不方便说明的信息。此外,可使用详细信息字段来提供更多信息。

错误详情

Google API 为错误详细信息定义了一组标准错误负载,您可在 google/rpc/error_details.proto 中找到这些错误负载。 它们涵盖了对于 API 错误的最常见需求,例如配额失败和无效参数。与错误代码一样,开发者应尽可能使用这些标准载荷。

只有在可以帮助应用代码处理错误的情况下,才应引入其他错误详细信息类型。如果错误信息只能由人工处理,则应根据错误消息内容,让开发人员手动处理,而不是引入其他错误详细信息类型。

下面是一些示例 error_details 载荷:

  • ErrorInfo 提供既稳定可扩展的结构化错误信息。
  • RetryInfo:描述客户端何时可以重试失败的请求,这些内容可能在以下方法中返回:Code.UNAVAILABLECode.ABORTED
  • QuotaFailure:描述配额检查失败的方式,这些内容可能在以下方法中返回:Code.RESOURCE_EXHAUSTED
  • BadRequest:描述客户端请求中的违规行为,这些内容可能在以下方法中返回:Code.INVALID_ARGUMENT

错误信息

ErrorInfo 是一种特殊类型的错误载荷。它可提供稳定且可扩展的错误信息,可供人工和应用参考。 每个 ErrorInfo 都包含三条信息:错误网域、错误原因和一组错误元数据,如示例所示。如需了解详情,请参阅 ErrorInfo 定义。

对于 Google API,主要错误网域是 googleapis.com,相应的错误原因由 google.api.ErrorReason 枚举定义。如需了解详情,请参阅 google.api.ErrorReason 定义。

错误本地化

google.rpc.Status 中的 message 字段面向开发人员,必须使用英语。

如果需要面向用户的错误消息,请使用 google.rpc.LocalizedMessage 作为您的详细信息字段。虽然 google.rpc.LocalizedMessage 中的消息字段可以进行本地化,请确保 google.rpc.Status 中的消息字段使用英语。

默认情况下,API 服务应使用经过身份验证的用户的语言区域设置或 HTTP Accept-Language 标头或请求中的 language_code 参数来确定本地化的语言。

错误映射

可以在不同的编程环境中访问 Google API。每种环境通常都有自己的错误处理方法。以下部分介绍了错误模型在常用环境中的映射方式。

HTTP 映射

虽然 proto3 消息具有原生 JSON 编码,但 Google 的 API 平台对 Google 的 JSON HTTP API 使用了不同的错误架构,以实现向后兼容性。

架构:

// This message defines the error schema for Google's JSON HTTP APIs.
message Error {
  // Deprecated. This message is only used by error format v1.
  message ErrorProto {}
  // This message has the same semantics as `google.rpc.Status`. It uses HTTP
  // status code instead of gRPC status code. It has extra fields `status` and
  // `errors` for backward compatibility with [Google API Client
  // Libraries](https://developers.google.com/api-client-library).
  message Status {
    // The HTTP status code that corresponds to `google.rpc.Status.code`.
    int32 code = 1;
    // This corresponds to `google.rpc.Status.message`.
    string message = 2;
    // Deprecated. This field is only used by error format v1.
    repeated ErrorProto errors = 3;
    // This is the enum version for `google.rpc.Status.code`.
    google.rpc.Code status = 4;
    // This corresponds to `google.rpc.Status.details`.
    repeated google.protobuf.Any details = 5;
  }
  // The actual error payload. The nested message structure is for backward
  // compatibility with [Google API Client
  // Libraries](https://developers.google.com/api-client-library). It also
  // makes the error more readable to developers.
  Status error = 1;
}

示例(链接):

{
  "error": {
    "code": 400,
    "message": "API key not valid. Please pass a valid API key.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "API_KEY_INVALID",
        "domain": "googleapis.com",
        "metadata": {
          "service": "translate.googleapis.com"
        }
      }
    ]
  }
}

gRPC 映射

不同的 RPC 协议采用不同方式映射错误模型。对于 gRPC,生成的代码和每种支持语言的运行时库为错误模型提供原生支持。您可在 gRPC 的 API 文档中了解更多信息。例如,请参阅 gRPC Java 的 io.grpc.Status

客户端库映射

Google 客户端库可能会根据语言选择采用不同方式表达错误,以与既定习语保持一致。例如,google-cloud-go 库将返回一个错误,该错误实现与 google.rpc.Status 相同的接口,而 google-cloud-java 将引发异常。

处理错误

下面的表格包含在 google.rpc.Code 中定义的所有 gRPC 错误代码及其原因的简短描述。要处理错误,您可以检查返回状态代码的说明并相应地修改您的调用。

HTTP gRPC 说明
200 OK 无错误。
400 INVALID_ARGUMENT 客户端指定了无效参数。如需了解详情,请查看错误消息和错误详细信息。
400 FAILED_PRECONDITION 请求无法在当前系统状态下执行,例如删除非空目录。
400 OUT_OF_RANGE 客户端指定了无效范围。
401 UNAUTHENTICATED 由于 OAuth 令牌丢失、无效或过期,请求未通过身份验证。
403 PERMISSION_DENIED 客户端权限不足。这可能是因为 OAuth 令牌没有正确的范围、客户端没有权限或者 API 尚未启用。
404 NOT_FOUND 未找到指定的资源。
409 ABORTED 并发冲突,例如读取/修改/写入冲突。
409 ALREADY_EXISTS 客户端尝试创建的资源已存在。
429 RESOURCE_EXHAUSTED 资源配额不足或达到速率限制。如需了解详情,客户端应该查找 google.rpc.QuotaFailure 错误详细信息。
499 CANCELLED 请求被客户端取消。
500 DATA_LOSS 出现不可恢复的数据丢失或数据损坏。客户端应该向用户报告错误。
500 UNKNOWN 出现未知的服务器错误。通常是服务器错误。
500 INTERNAL 出现内部服务器错误。通常是服务器错误。
501 NOT_IMPLEMENTED API 方法未通过服务器实现。
502 不适用 到达服务器前发生网络错误。通常是网络中断或配置错误。
503 UNAVAILABLE 服务不可用。通常是服务器已关闭。
504 DEADLINE_EXCEEDED 超出请求时限。仅当调用者设置的时限比方法的默认时限短(即请求的时限不足以让服务器处理请求)并且请求未在时限范围内完成时,才会发生这种情况。

重试错误

客户端可能使用指数退避算法重试 503 UNAVAILABLE 错误。 除非另有说明,否则最小延迟应为 1 秒。 除非另有说明,否则默认重试重复应当仅一次。

对于 429 RESOURCE_EXHAUSTED 错误,客户端可能会在更高层级以最少 30 秒的延迟重试。此类重试仅对长时间运行的后台作业有用。

对于所有其他错误,重试请求可能并不适用。首先确保您的请求具有幂等性,并查看 google.rpc.RetryInfo 以获取指导。

传播错误

如果您的 API 服务依赖于其他服务,则不应盲目地将这些服务的错误传播到您的客户端。在翻译错误时,我们建议执行以下操作:

  • 隐藏实现详细信息和机密信息。
  • 调整负责该错误的一方。例如,从另一个服务接收 INVALID_ARGUMENT 错误的服务器应该将 INTERNAL 传播给它自己的调用者。

重现错误

如果您通过分析日志和监控功能无法解决错误,则应该尝试通过简单且可重复的测试来重现错误。您可以使用该测试来收集问题排查的相关信息,您可以在联系技术支持团队时提供这些信息。

我们建议您使用 curl -v系统参数来重现 Google API 错误。它们可以共同重现几乎所有 Google API 请求,并为您提供详细的调试信息。如需了解详情,请参阅您要调用的 API 的相应文档页面。

生成错误

如果您是服务器开发者,则应该生成包含足够信息的错误,以帮助客户端开发者理解并解决问题。同时,您必须重视用户数据的安全性和隐私性,避免在错误消息和错误详细信息中披露敏感信息,因为错误通常会被记录下来并且可能被其他人访问。例如,诸如“客户端 IP 地址不在 allowlist 128.0.0.0/8 上”之类的错误消息会披露服务器端政策的相关信息,用户可能无法访问日志。

要生成正确的错误,首先需要熟悉 google.rpc.Code,然后才能为每个错误条件选择最合适的错误代码。服务器应用可以并行检查多个错误条件,并返回第一个错误条件。

下表列出了每个错误代码和恰当的错误消息示例。

HTTP gRPC 错误消息示例
400 INVALID_ARGUMENT 请求字段 x.y.z 是 xxx,预期为 [yyy, zzz] 内的一个。
400 FAILED_PRECONDITION 资源 xxx 是非空目录,因此无法删除。
400 OUT_OF_RANGE 参数“age”超出范围 [0,125]。
401 UNAUTHENTICATED 身份验证凭据无效。
403 PERMISSION_DENIED 使用权限“xxx”处理资源“yyy”被拒绝。
404 NOT_FOUND 找不到资源“xxx”。
409 ABORTED 无法锁定资源“xxx”。
409 ALREADY_EXISTS 资源“xxx”已经存在。
429 RESOURCE_EXHAUSTED 超出配额限制“xxx”。
499 CANCELLED 请求被客户端取消。
500 DATA_LOSS 请参阅备注。
500 UNKNOWN 请参阅备注。
500 INTERNAL 请参阅备注。
501 NOT_IMPLEMENTED 方法“xxx”未实现。
503 UNAVAILABLE 请参阅备注。
504 DEADLINE_EXCEEDED 请参阅备注。

错误负载

google.rpc 软件包定义了一组标准错误载荷,它们优先于自定义错误载荷。下表列出了每个错误代码及其匹配的标准错误负载(如果适用)。 我们建议高级应用在处理错误时在 google.rpc.Status 中查找这些错误负载。

HTTP gRPC 建议的错误详细信息
400 INVALID_ARGUMENT google.rpc.BadRequest
400 FAILED_PRECONDITION google.rpc.PreconditionFailure
400 OUT_OF_RANGE google.rpc.BadRequest
401 UNAUTHENTICATED google.rpc.ErrorInfo
403 PERMISSION_DENIED google.rpc.ErrorInfo
404 NOT_FOUND google.rpc.ResourceInfo
409 ABORTED google.rpc.ErrorInfo
409 ALREADY_EXISTS google.rpc.ResourceInfo
429 RESOURCE_EXHAUSTED google.rpc.QuotaFailure
499 CANCELLED
500 DATA_LOSS google.rpc.DebugInfo
500 UNKNOWN google.rpc.DebugInfo
500 INTERNAL google.rpc.DebugInfo
501 NOT_IMPLEMENTED
503 UNAVAILABLE google.rpc.DebugInfo
504 DEADLINE_EXCEEDED google.rpc.DebugInfo