应使用 errors.is(err, os.errnotexist) 或 errors.is(err, os.errpermission) 判断权限不足或文件不存在,因 os.open 返回的错误是包装过的,直接用 == 比较会失败。

怎么判断 os.Open 是权限不足还是文件不存在
Go 的 os.Open 返回的 *os.PathError 包含底层错误,但直接用 err == os.ErrNotExist 或 err == os.ErrPermission 会失败——因为实际错误是包装过的。必须用 errors.Is 做语义判断。
- 正确写法:
if errors.Is(err, os.ErrNotExist) { ... } -
os.ErrPermission同理,不能用==比对原始值 - 某些系统(如 Windows)可能返回
syscall.ERROR_ACCESS_DENIED,errors.Is(err, os.ErrPermission)仍能识别 - 别用
strings.Contains(err.Error(), "permission")—— 语言/环境不同会导致字符串不一致
重试时哪些 I/O 错误值得等,哪些该立刻放弃
不是所有 error 都适合重试。关键看错误是否可能随时间自动恢复,比如网络文件系统短暂挂起、NFS stale handle、临时磁盘满等;而像 os.ErrNotExist 或路径语法错误,重试一百次也没用。
- 建议重试:
syscall.EAGAIN、syscall.EWOULDBLOCK、syscall.ENETUNREACH、syscall.ETIMEDOUT - 绝对不重试:
os.ErrNotExist、os.ErrInvalid、syscall.ENOTDIR、syscall.EISDIR - 用
errors.Is(err, syscall.EAGAIN)判断,别依赖err.(syscall.Errno)类型断言——有些错误是包装过的 - 重试前加指数退避,避免雪崩;最多 3–5 次,间隔从 10ms 起步
io.ReadFull 和 io.ReadAtLeast 的错误含义容易混淆
这两个函数不按“读完就成功”来返回错误,而是严格按字节数契约判错,新手常误以为它们和 Read 一样只在 EOF 或系统错误时出错。
-
io.ReadFull:要求**恰好读满**指定字节数,少一个字节就返回io.ErrUnexpectedEOF(不是io.EOF) -
io.ReadAtLeast:要求**至少读到**指定字节数,若读到但不足,返回io.ErrShortBuffer;若一次都读不到且 EOF,才返回io.EOF - 注意:
io.ErrUnexpectedEOF和io.EOF是两个不同变量,不能混用errors.Is(err, io.EOF)去捕获前者 - 常见坑:把
io.ReadFull用于网络连接时,对方提前关闭连接,就会触发io.ErrUnexpectedEOF,需单独处理
Windows 下路径分隔符和长路径错误的实际影响
Go 在 Windows 上默认支持长路径(>260 字符),但前提是程序 manifest 正确声明,且路径以 \? 前缀开头;否则仍会因路径过长或含非法字符(如 :、)直接失败。
立即学习“go语言免费学习笔记(深入)”;
- 调用
os.Open前,可用filepath.Abs规范路径,再检查长度:if len(path) > 260 && runtime.GOOS == "windows" { ... } - 对确定超长的路径,手动加前缀:
"\\?\" + filepath.ToSlash(path)(注意双反斜杠转义) - 错误信息里出现
The system cannot find the path specified,不一定是路径不存在——很可能是长度超限被截断 -
os.MkdirAll对嵌套深的目录也容易失败,建议逐级创建并检查每层返回的err是否为os.ErrExist










