错误

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

Google API 使用简单的协议无关错误模型,以便我们在不同的 API、API 协议(如 gRPC 或 HTTP)和错误上下文(例如异步、批处理或工作流错误)中能够有一致的体验。

错误模型

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

package google.rpc;

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 delay 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 调用平均处理 3 个错误代码意味着大多数应用的逻辑只是用于错误处理,这对开发人员而言并非好体验。

错误消息

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

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

错误详情

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

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

下面是一些示例 error_details 载荷:

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

错误信息

ErrorInfo 是标准错误载荷之一。它提供人类和应用可使用的稳定且可扩展错误信息。每个 ErrorInfo 包含 3 条信息:错误域、错误原因和一组错误元数据。如需了解详情,请参阅 ErrorInfo 定义。

HTTP 映射

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

架构:

// The error schema for Google REST APIs. NOTE: this schema is not used for
// other wire protocols.
message Error {
  // This message has the same semantics as `google.rpc.Status`. It has an extra
  // field `status` for backward compatibility with Google API Client Library.
  message Status {
    // This corresponds to `google.rpc.Status.code`.
    int32 code = 1;
    // This corresponds to `google.rpc.Status.message`.
    string message = 2;
    // 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. It also makes the error
  // more readable to developers.
  Status error = 1;
}

示例:

{
  "error": {
    "code": 401,
    "message": "Request had invalid credentials.",
    "status": "UNAUTHENTICATED",
    "details": [{
      "@type": "type.googleapis.com/google.rpc.RetryInfo",
      ...
    }]
  }
}

RPC 映射

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

客户端库映射

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

错误本地化

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

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

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

处理错误

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

HTTP RPC 说明
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 方法未通过服务器实现。
503 UNAVAILABLE 服务不可用。通常是服务器已关闭。
504 DEADLINE_EXCEEDED 超出请求时限。仅当调用者设置的时限比方法的默认时限短(即请求的时限不足以让服务器处理请求)并且请求未在时限范围内完成时,才会发生这种情况。

错误重试

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

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

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

错误传播

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

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

生成错误

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

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

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

HTTP RPC 错误消息示例
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 软件包定义了一组标准错误载荷,它们优先于自定义错误载荷。下表列出了每个错误代码及其匹配的标准错误负载(如果适用)。

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