Kratos 中间件 panic 导致服务启动失败,主因是中间件构造函数或 Handler 内非法断言、nil 指针解引用、未初始化依赖(如 logger);框架不 recover,panic 直接冒泡至 main goroutine。

中间件 panic 导致服务启动失败
kratos.New() 启动时直接崩溃,常见原因是中间件构造函数或 Handler 内部 panic,而框架未捕获——比如在 middleware.Middleware 返回的闭包里做了非法类型断言、访问了 nil 指针,或调用了未初始化的 logger。
关键点:Kratos 不会对中间件做 recover,panic 会直接冒泡到 main goroutine。排查时先注释掉所有自定义中间件,再逐个启用;同时检查中间件是否依赖了未注入的依赖(如 logger、tracer),这些变量若为 nil,log.NewHelper(nil) 或 trace.FromContext(ctx) 都会 panic。
- 确保中间件构造函数参数都做了非空校验,例如
if logger == nil { panic("logger required") } - 不要在中间件闭包外提前调用
log.NewHelper(logger)——logger 可能为 nil,应放到 Handler 内部首次使用前再初始化 - 避免对
req做(*http.Request)断言:它其实是*bm.Context或其他 transport 封装体,正确方式是用transport.FromServerContext(ctx)获取 transporter
中间件里 ctx.Value() 为空引发空指针崩溃
自定义中间件中调用 ctx.Value(key) 返回 nil,后续直接解引用(如 v.(*User).ID)就会 panic。这不是 Kratos 的 bug,而是 context 透传没做对。
Kratos 的 HTTP 中间件接收的是标准 *http.Request 的 context,你往全局 context 或 transport.HTTPContext() 里塞的值不会自动出现。必须显式把值注入 request context 才能被下游拿到。
- 前置中间件要写:
r = r.WithContext(context.WithValue(r.Context(), key, val)),再调next.ServeHTTP(w, r) - 别用
transport.HTTPContext()——它返回的是空 context,不是 request 绑定的那个 - 如果用的是
transport.HTTPRequestContext(r),它虽含 transport info,但依然不包含你塞的业务数据,仍需手动WithValue
中间件嵌套过深或递归调用导致栈溢出
看似正常的中间件组合,运行一阵后出现 runtime: goroutine stack exceeds 1000000000-byte limit,大概率是中间件链形成了隐式递归,比如在中间件里又触发了同一路由(如重定向、内部 client 调用本服务 HTTP 接口),而该请求又被同一组中间件拦截。
典型场景:鉴权中间件发现 token 过期,自动调用 /v1/auth/refresh 刷新令牌,但这个路径没排除在中间件之外,导致 refresh 请求再次进入鉴权逻辑,无限循环。
- 用
transport.FromServerContext(ctx).Operation()检查当前操作名,对内部调用路径做白名单跳过 - 避免在中间件里发起本服务的同步 HTTP 调用;改用异步通知、事件或直接调 biz 层函数
- 给中间件加深度计数器(如 ctx 值里存
mwDepth),超阈值(如 >5)直接 return error 阻断
中间件修改 response header 或 write 时已提交状态
中间件后置逻辑里调 w.Header().Set() 或 w.Write() 报错 http: superfluous response.WriteHeader call 或 http: response wrote more than the declared Content-Length,说明 response 已被下游 handler 提交(即已调 w.WriteHeader 或 w.Write)。
Kratos 的 http.Server 默认使用 bm.Context 封装响应流程,但中间件若直接操作 http.ResponseWriter,就绕过了框架控制。一旦业务 handler 先写了 body,中间件再写就冲突。
- 后置逻辑只能读取 response 状态(如
w.Header().Get("Content-Type")),不能修改或写 body - 如需注入 header(如
X-Processed-By),必须在transport.FromServerContext(ctx)成功断言为*http.Transporter后,调c.Response().Header().Set()——这是 Kratos 安全的响应操作入口 - 绝对不要在中间件里调
w.WriteHeader()或w.Write(),这会破坏框架错误编码和 status 映射逻辑


















