kratos 错误处理由 protobuf 注解驱动,须在 .proto 中用 (errors.code) 定义 errorreason 枚举;业务层调用 errors.new(int32(pb.enum), msg) 构造错误;手动设状态码或裸 error 会绕过框架,导致日志、监控、文档、grpc 状态码失效。

Kratos 的错误处理不是靠中间件“兜底”,而是由 Protobuf 定义驱动、框架自动注入的。直接写 http.Error 或手动 w.WriteHeader 会绕过整个错误体系,导致日志、监控、Swagger 文档、gRPC 状态码全部失效。
Protobuf 中定义 ErrorReason 枚举是前提
必须在 .proto 文件里声明带 (errors.code) 注解的枚举,Kratos 才能识别并生成对应错误码逻辑:
-
ErrorReason枚举需 import"errors/errors.proto" - 每个枚举值必须显式标注
(errors.code) = NNN,例如USER_NOT_FOUND = 0 [(errors.code) = 404]; - 不加注解的枚举值会被忽略,框架不会为其生成 HTTP 状态码或 gRPC
Status - 如果没定义
ErrorReason,errors.New抛出的 error 默认映射为 500,且无reason字段
errors.New + ErrorReason 枚举值才是标准用法
业务层不要自己拼 JSON 或设状态码,而是用 Kratos 提供的 errors 包构造错误:
- 调用
errors.New(int, string)时,第一个参数必须是ErrorReason枚举的整数值(不是字符串名) - 例如:返回
USER_NOT_FOUND,应写errors.New(int32(pb.USER_NOT_FOUND), "user not found"),而不是"USER_NOT_FOUND" - 该 error 会被 transport 层自动转成含
code、reason、message、metadata的标准响应体 - HTTP 响应头
Status和 gRPCStatus.Code全部由框架根据(errors.code)注解推导,无需手动设置
自定义错误格式需改 transport/http/server.go 的 errorEncoder
默认响应结构是 {"code":404,"reason":"USER_NOT_FOUND","message":"..."}。如需改成 {"err_code":...,"err_msg":...} 这类字段名,不能动业务代码,而要重写 encoder:
- 找到项目中初始化 HTTP server 的地方(通常是
cmd/xxx/main.go) - 在
http.Server配置里传入自定义errorEncoder函数 - 函数签名必须是
func(ctx context.Context, err error, w http.ResponseWriter) - 内部仍需调用
errors.FromError(err)解析原始错误,再按需序列化 - 注意:改了
errorEncoder不影响 gRPC 的错误编码,gRPC 走的是另一套grpc.Server的errorCodec
中间件里捕获 panic 或未知 error 时务必用 errors.New 包装
有些场景(如 biz 层 panic、第三方库返回裸 error)无法走标准枚举路径,这时中间件兜底要谨慎:
- 不要直接
return nil, err—— 框架可能无法识别裸 error,降级为 500 且无reason - 推荐统一 fallback 到一个兜底枚举值,比如
UNKNOWN_ERROR = 999 [(errors.code) = 500] - 然后包装:
errors.New(int32(pb.UNKNOWN_ERROR), fmt.Sprintf("panic: %v", recover())) - 避免在中间件里手动写
w.WriteHeader(500)+json.Marshal,否则会和 transport 冲突,造成重复写 body
最易被忽略的一点:Protobuf 枚举值变更后,必须重新运行 kratos proto client 或 protoc 生成 Go 代码,否则 pb.USER_NOT_FOUND 常量不会更新,运行时传错数字会导致错误码映射完全错乱。











