必须用 errors.Is(err, syscall.ENOSPC) 判断磁盘满错误,因标准库仅识别系统错误码;伪造 ENOSPC 应在 io.Writer 层实现,注意 bufio.Flush、io.Closer 和部分写处理。

磁盘空间不足错误不能靠 errors.New("no space") 模拟或捕获,必须用 syscall.ENOSPC(Linux/macOS)或 syscall.ERROR_DISK_FULL(Windows)——否则上层逻辑根本识别不了这是磁盘满,只会当普通 I/O 错误吞掉。
为什么 errors.Is(err, syscall.ENOSPC) 是唯一可靠判断方式
Go 标准库和主流日志/文件库(如 lumberjack、os.WriteFile)只对系统级错误码做分支处理。比如 os.WriteFile 内部遇到写失败时,仅当 err 是 syscall.ENOSPC 才可能触发重试或清理;返回 errors.New("disk full") 或字符串匹配 "no space left" 都会被忽略。
-
strings.Contains(err.Error(), "no space")在非英文 locale 下失效,且易误匹配路径或日志内容 -
os.IsNoSpace(err)并不存在 —— Go 没提供这个 helper,得手动用errors.Is - 容器环境还要额外检查 inodes:用
unix.Statfs看AvailInodes,光看字节空间不够
测试时怎么稳定复现 ENOSPC 错误
别去塞满真实磁盘:慢、不可控、需 root 权限、清理麻烦。直接在 I/O 接口层伪造更轻量可靠。
- 实现一个
io.Writer,在写入指定字节数后返回syscall.ENOSPC(Linux/macOS)或syscall.ERROR_DISK_FULL(Windows) - 若被测代码用
io.Copy,注意它对短写(n < len(p)且err == nil)的处理 —— 测试时应优先触发错误,而非返回部分成功 - 替换
os.Stdout时,mock writer 必须同时实现io.Closer,Close()返回nil,否则调用方os.Stdout.Close()会 panic - 如果代码用了
bufio.Writer包裹,测试前记得调用Flush(),否则错误卡在缓冲区里不抛出
写入后不检查 n 值会导致磁盘满被静默掩盖
Write() 和 WriteString() 都返回 (int, error)。网络文件系统、满磁盘、信号中断等场景下,可能只写入部分数据并返回 nil 错误 —— 这是合法行为,但极易被忽略。
立即学习“go语言免费学习笔记(深入)”;
- 只判
err != nil不够:Write()可能返回n=0, err=nil(例如磁盘刚满,内核缓存还没刷),后续再写才报ENOSPC - 关键路径建议用
io.WriteString()替代裸Write(),它内部已做补全逻辑 - 对
io.Copy,要配合自定义io.Writer并在Write方法里主动检查累计写入量是否已达阈值,及时返回ENOSPC
真正难处理的不是“怎么报错”,而是“报错之后要不要继续写”。比如日志系统在磁盘满时该降级到内存缓冲、切到备用存储,还是直接 panic?这取决于你的 SLA,但前提是——你得先让错误浮出水面,而不是被 log.SetOutput 或 lumberjack 静默吃掉。


















