
go 中 `os.write` 返回的错误通常是 `*os.patherror`,需通过类型断言提取其 `err` 字段,并与 `syscall.enospc` 比较,才能可靠识别磁盘满错误。
在 go 语言中,底层系统调用失败时,标准库(如 os.File.Write)不会直接返回 syscall.errno 类型错误,而是封装为更语义化的 *os.PathError。该结构体包含操作名、路径及底层系统错误 Err 字段——而这个 Err 才可能是 syscall.Errno。
因此,正确检测 ENOSPC 的方式是:先对 err 进行 *os.PathError 类型断言,再检查其 Err 字段是否等于 syscall.ENOSPC。以下为修正后的完整示例:
package main import ( "log" "os" "syscall" ) func main() { fd, err := os.Create("dump.txt") if err != nil { log.Fatal("failed to create file:", err) } defer fd.Close() for { buf := make([]byte, 1024) _, err := fd.Write(buf) if err != nil { log.Printf("Write error: %v", err) // 正确方式:断言 *os.PathError,再检查其 Err 字段 if pathErr, ok := err.(*os.PathError); ok { if errno, ok := pathErr.Err.(syscall.Errno); ok && errno == syscall.ENOSPC { log.Println("❌ Critical: No space left on device (ENOSPC)") return } } // 可选:处理其他常见系统错误(如权限不足、设备忙等) log.Printf("Unexpected error type: %T, value: %v", err, err) return } } }
✅ 关键点说明:
- err.(*os.PathError) 是第一层断言,因为 os.Write 在文件操作失败时几乎总是返回此类型;
- pathErr.Err 是嵌套的底层错误,需再次断言为 syscall.Errno 才能进行数值比较;
- 直接 err.(syscall.Errno) 失败(如原问题中所示),是因为 err 并非裸 syscall.Errno,而是被 os.PathError 包装过;
- 建议始终检查两次断言结果(ok),避免 panic;
⚠️ 注意事项:
- syscall.ENOSPC 是 unix/linux/macOS 常用常量;在 windows 上对应错误码为 syscall.ERROR_HANDLE_DISK_FULL 或 syscall.ERROR_DISK_FULL,但 Go 标准库通常会统一映射为 ENOSPC(自 Go 1.13+ 起,os.IsNoSpace 已提供跨平台抽象);
- 更推荐的现代写法(Go 1.13+):使用 errors.Is(err, syscall.ENOSPC) 或 os.IsNoSpace(err),它内部已自动处理包装层级:
if os.IsNoSpace(err) { log.Println("Out of disk space — handle gracefully") }
该方法更简洁、可读性强,且具备跨平台兼容性,是当前最佳实践。