grpc status code 透传失败主因是错误包装不一致:下游须用 status.errorf() 构造错误,中间服务禁用 status.convert(err).err() 降级,details 需显式注册且拦截器不可抹除。

gRPC Status Code 在跨服务调用中为什么总是变成 UNKNOWN?
因为 status.FromError() 只能解析 gRPC 自带的 status.Status 错误,而下游服务若直接返回 errors.New("xxx") 或自定义 error,上游拿到的就是 status.Code() == codes.Unknown。透传失败不是网络问题,是错误包装方式不一致。
- 下游必须用
status.Errorf(codes.XXX, "msg")构造错误,不能用fmt.Errorf或裸errors.New - 如果下游是 HTTP 服务转接 gRPC,需在代理层手动将 HTTP 状态码/错误体映射为
status.Status - Go 的
error接口无标准 code 字段,gRPC 不会自动“猜”你本意是 Internal 还是 NotFound
如何让下游错误的 code 和 details 完整透传到最上层客户端?
关键在中间服务不做「错误降级」:别把 status.Status 转成普通 error 再抛出,也别用 status.Convert(err).Err() 反向转换——这会丢掉 Details。
- 下游返回前:用
status.WithDetails(status.Status, &errdetails.ErrorInfo{Reason: "AUTH_FAILED"})补充结构化信息 - 中间服务转发时:直接 return 原始 error(前提是它确实是
*status.Status) - 若中间层必须加日志或修饰,用
status.WithMessage(st, "[auth] "+st.Message()),避免破坏原有 code 和 details
Go 客户端怎么安全提取透传的 status code 和 details?
别直接 err.Error() 判断字符串,也不该依赖 errors.Is(err, xxx) —— 那些都绕过了 gRPC 的 status 机制。
- 先用
status.FromError(err)解包,检查ok == true,否则就是非 gRPC 错误(比如连接超时) - 用
st.Code()拿 code,比 switcherr类型更可靠 - 读 details 时遍历
st.Details(),注意每个 item 是proto.Message,需类型断言,例如:if info, ok := detail.(*errdetails.ErrorInfo); ok { ... }
为什么加了 WithDetails 客户端还是收不到 details?
两个常见断点:序列化失败或拦截器吞掉了。gRPC 默认只允许已注册的 proto message 类型进 details,未注册的会被静默丢弃。
立即学习“go语言免费学习笔记(深入)”;
- 所有要放进 details 的 proto struct,必须在 main 包或 init 里显式调用
errdetails.Register(&errdetails.ErrorInfo{}) - 若用了自定义拦截器(比如统一日志),检查是否对 error 做了
status.Convert().Err()或fmt.Sprintf("%v", err)—— 这两种操作都会抹掉 details - 客户端侧也要确保 proto runtime 版本兼容,
google.golang.org/genproto/googleapis/rpc/errdetails包版本不一致会导致 details 解析失败
细节注册和拦截器处理是实际项目中最容易漏掉的两环,错一个,details 就彻底消失。










