应传 error 类型值给 panic(),它会自动调用 error() 输出字符串;传字符串等非 error 类型会丢失类型信息,影响 recover 时的类型断言和错误链追溯。

panic 传入 error 类型值就能自定义提示内容
直接把 error 值传给 panic(),它会自动调用 Error() 方法输出字符串,而不是打印原始类型名或内存地址。这是最干净、最符合 Go 惯用法的方式。
常见错误是传字符串字面量(如 panic("config not found")),虽然能运行,但丢失了类型信息,后续无法做类型断言或结构化处理。
- ✅ 正确:用
errors.New或fmt.Errorf构造error后再 panic - ✅ 正确:panic 自定义错误类型指针(如
&ConfigError{...}) - ❌ 错误:直接 panic 字符串、整数、结构体字面量(除非明确需要非 error 类型)
err := fmt.Errorf("failed to load config: %w", io.ErrUnexpectedEOF)
panic(err) // 输出 "failed to load config: unexpected EOF"
自定义错误类型 panic 后可被 recover 精准识别
如果你在框架或中间件里用 recover() 捕获 panic,只有 panic 的值是 error 类型(或你自定义的错误类型),才能安全地做类型断言。否则 err.(type) 会 panic 本身。
比如 Gin 中写 recovery 中间件时,常这样判断:
-
err.(*AppError)→ 返回 400/401 等业务状态码 -
err.(error)→ 降级为 500,但至少保留Error()文本 -
err != nil且不是以上两类 → 视为未知 panic,记录日志并返回通用错误
if e, ok := err.(*ValidationError); ok {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": e.Error()})
}
不要在 panic 里传 nil error
panic(nil) 是合法语法,但会导致 recover() 返回 nil,极易被忽略——你以为捕获到了,其实什么都没拿到。
更危险的是,有人写成 if err != nil { panic(err) },但 err 是未初始化的 error 变量(即 nil),结果 panic 了 nil,整个 recover 分支完全失效。
- 检查 panic 前是否真的有非 nil error:加个
if err != nil再 panic - 避免直接
panic(err)而不校验,尤其当err来自函数返回且可能为 nil 时 - 测试时故意触发 panic 场景,确认 recover 能拿到非 nil 值
fmt.Errorf 包装底层 error 时注意 %w 动词
如果想让 panic 的错误链可追溯(比如上层 panic 包含下层 os.Open 的真实错误),必须用 %w 而不是 %s 或 %v。
用了 %w,后续可以用 errors.Is() 或 errors.As() 判断是否包装了某个底层错误;没用就断链,只剩字符串描述。
// ✅ 可追溯
panic(fmt.Errorf("loading config failed: %w", os.ErrNotExist))
// ❌ 断链,只剩文字
panic(fmt.Errorf("loading config failed: %s", os.ErrNotExist))
真正容易被忽略的点是:panic 不是终点,而是错误传播的起点;你传进去的内容,决定了下游 recover 能拿到什么、能做什么判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











