Write() 不暴露 ENOSPC,真正错误在 Flush() 和 Close();需用 errors.Is(err, syscall.ENOSPC) 跨平台检测,且必须检查二者返回值。

Write() 返回的 err 不是 syscall.ENOSPC,别直接断言
Go 的 os.File.Write 失败时,几乎总返回 *os.PathError,而不是裸的 syscall.Errno。直接写 err.(syscall.Errno) == syscall.ENOSPC 会 panic 或永远不匹配。
真正要检查的是 *os.PathError 的 Err 字段:
- 先做类型断言:
if pathErr, ok := err.(*os.PathError); ok - 再比对其
Err字段:pathErr.Err == syscall.ENOSPC - 注意:
syscall.ENOSPC在 Windows 上不可用,跨平台项目需加构建约束或改用errors.Is(err, syscall.ENOSPC)(Go 1.13+)
bufio.Writer.Flush() 才暴露磁盘满错误,Write() 不报
bufio.Writer 的 Write() 方法几乎总是返回 nil 错误,哪怕磁盘已满——因为数据还在内存缓冲区里。真正的落盘失败(包括 ENOSPC)只在调用 Flush() 时触发。
这意味着:
立即学习“go语言免费学习笔记(深入)”;
- 忘记
Flush(),程序跑完文件可能为空或截断,且无任何错误提示 -
defer w.Flush()在 panic 或提前return时不会执行,数据静默丢失 - 必须把
Flush()放在关键路径上,并检查其返回的err
Close() 也可能返回 ENOSPC,不能只检查 Write/Flush
即使 Write() 和 Flush() 都成功,file.Close() 仍可能因内核延迟刷盘失败而返回 ENOSPC(尤其在 ext4 等日志型文件系统上)。
所以:
- 写完后不能只依赖
Flush()就认为安全;Close()的返回值也必须检查 - 不要把
Close()仅放在defer里——defer只保证调用,不保证你看到错误 - 推荐模式:
if err := w.Flush(); err != nil { /* handle */ }+if err := f.Close(); err != nil { /* handle */ }
批量写入时如何避免反复检测 ENOSPC
高频小写入场景下,每次 Write → Flush → 检查 ENOSPC 效率低,且无法预测下次是否失败。更稳妥的做法是:
- 写入前预估剩余空间:用
syscall.Statfs查目标路径所在文件系统的Bavail - 按块写入(如 4KB~64KB),每写若干块后
Flush()并检查错误 - 一旦捕获
ENOSPC,立即停止写入、清理临时状态、通知上层(如返回自定义错误或触发告警) - 注意:
Statfs结果有滞后性,不能替代实际写入时的错误检查
实际落盘失败往往藏在 Flush() 和 Close() 里,而不是 Write() 调用那一刻。ENOSPC 是个“延迟暴露”的错误,靠一次判断远远不够。


















