recover无法捕获磁盘满错误,因为os.writefile、f.write等函数只返回含syscall.enospc的error而不panic;必须用errors.is(err, syscall.enospc)跨平台判断,辅以n值检查、flush调用及inodes监控。

不能靠 recover 捕获磁盘满错误,因为 os.WriteFile、f.Write 等函数根本不会 panic —— 它们只返回 error。试图用 defer + recover 拦截 ENOSPC 是无效的。
为什么 recover 对磁盘满完全没用
Go 标准库所有文件写入操作(os.WriteFile、f.Write、io.Copy)在遇到磁盘空间不足时,都返回带 syscall.ENOSPC 的 error,而不是触发 panic。recover 只能捕获显式调用 panic() 或运行时崩溃(如 nil pointer dereference),对 I/O 错误无感知。
-
panic("no space")是人工写的,标准库不会这么干 - 你写的业务代码若没主动
panic(err),那 recover 就永远等不到它 - 依赖 recover 会导致真正该处理的
errors.Is(err, syscall.ENOSPC)被跳过,错误静默吞掉
必须用 errors.Is(err, syscall.ENOSPC) 判断磁盘满
这是唯一跨平台、可移植、被标准库和主流日志库(如 lumberjack)识别的判断方式。字符串匹配(如 strings.Contains(err.Error(), "no space"))在中文 locale、容器环境或自定义 error 包裹下必然失效。
- Linux/macOS 用
syscall.ENOSPC;Windows 用syscall.ERROR_DISK_FULL -
os.IsNoSpace(err)不存在 —— Go 官方没提供这个 helper 函数 - 如果用了
bufio.Writer,记得先调用Flush(),否则错误卡在缓冲区里不暴露 - 容器环境还要额外检查 inodes:用
unix.Statfs看AvailInodes,光看字节空间不够
写入后不检查 n 值会掩盖磁盘满
Write() 和 WriteString() 都返回 (int, error)。磁盘刚满时,内核可能接受 write 系统调用但只刷出部分数据,返回 n 且 <code>err == nil —— 这是 POSIX 合法行为,但极易被忽略。
- 只判
err != nil不够:可能n == 0 && err == nil,下一次写才爆ENOSPC - 关键路径建议用
io.WriteString()替代裸Write(),它内部已做补全逻辑 - 对
io.Copy,需自定义io.Writer并在Write方法里主动累加写入量,到阈值后直接返回syscall.ENOSPC
测试时别塞满真实磁盘,用伪造 Writer 更可靠
真实塞盘慢、需 root、不可控、清理麻烦。应在 I/O 接口层伪造:实现一个 io.Writer,在写入指定字节数后返回系统级错误码。
- Linux/macOS 返回
syscall.ENOSPC;Windows 返回syscall.ERROR_DISK_FULL - 若替换
os.Stdout,mock writer 必须同时实现io.Closer,Close()返回nil,否则os.Stdout.Close()会 panic - 注意
io.Copy对短写(n )的处理逻辑 —— 测试应优先触发错误,而非返回部分成功
真正难的不是“怎么发现磁盘满”,而是“发现之后要不要继续写”。比如日志系统该降级到内存缓冲、切备用存储,还是拒绝新请求?这些决策点不会出现在 error 判断里,但一旦漏掉,再准的 errors.Is 也救不了服务。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











