os.Open 返回 *os.PathError 的典型场景是打开不存在的路径,此时 Err 字段为 ENOENT、Path 字段记录路径;须用 errors.Is(err, os.ErrNotExist) 而非 == 判断,以支持错误包装;权限不足用 errors.Is(err, os.ErrPermission);检查存在性应优先用 os.Stat 配合 os.IsNotExist/os.IsPermission;os.OpenFile 的 flag 影响错误类型,如 os.O_CREATE 不创建父目录;跨平台需统一用 errors.Is 或 os.IsXXX 函数判断错误;Windows 长路径需启用支持并加 \\?\ 前缀;符号链接失效时 os.Stat 报 os.ErrNotExist,os.Lstat 可区分链接本身是否存在。
调用 os.Open 打开一个不存在的路径时,Golang 不会 panic,而是返回 *os.PathError,其中 Err 字段是底层系统错误(如 ENOENT),Path 字段记录出错的具体路径。这类错误必须显式检查,否则程序可能在后续读取时才崩溃。
err == os.ErrNotExist 可判断“路径不存在”,但仅适用于 os.Stat、os.Open 等明确返回该变量的函数errors.Is(err, os.ErrNotExist) —— 它能穿透包装错误(比如被 fmt.Errorf("failed: %w", err) 包裹后仍可识别)err == os.ErrPermission 或 errors.Is(err, os.ErrPermission),但注意:在 Windows 上该错误不常用,实际可能是 ERROR_ACCESS_DENIED
不要依赖 os.Open 成功与否来判断存在性,因为即使路径存在,也可能因权限/符号链接循环等问题失败。应先用 os.Stat 获取元信息,并结合 os.IsNotExist 和 os.IsPermission 做分类处理。
fi, err := os.Stat("/path/to/file")
if err != nil {
if errors.Is(err, os.ErrNotExist) {
log.Printf("路径不存在: %s", "/path/to/file")
return
}
if errors.Is(err, os.ErrPermission) {
log.Printf("无访问权限: %s", "/path/to/file")
return
}
log.Printf("其他 stat 错误: %v", err)
return
}
if !fi.Mode().IsRegular() {
log.Printf("不是常规文件: %s", "/path/to/file")
return
}
os.OpenFile 的 flag 参数直接影响报错行为。例如使用 os.O_CREATE | os.O_WRONLY 时,若父目录不存在,会返回 no such file or directory(ENOENT),而不是尝试创建目录;而用 os.O_CREATE | os.O_RDWR 打开只读文件,则可能触发 permission denied(EACCES)。
os.O_CREATE 单独使用不会自动创建父目录,需配合 os.MkdirAll
os.O_EXCL | os.O_CREATE 组合用于“原子性创建”,若文件已存在则返回 os.ErrExist

os.Stat(dir); err == nil && fi.IsDir() && (fi.Mode()&0200 != 0)(检查用户写位)Windows 和 Unix-like 系统对路径错误的底层 errno 映射不同。例如:os.IsNotExist(err) 在 Windows 上会检查 ERROR_PATH_NOT_FOUND 和 ERROR_FILE_NOT_FOUND,而 Unix 下只看 ENOENT。这意味着你不能硬编码字符串匹配错误信息。
errors.Is(err, ...) 或 os.IsXXX(err) 系列函数,而非 strings.Contains(err.Error(), "no such")
\\?\ 前缀,否则 os.Stat 可能静默失败或返回奇怪错误os.Stat 报 os.ErrNotExist,而 os.Lstat 可成功读取链接本身 —— 这影响你是否需要区分“链接存在但目标失效”os.Open 和 os.Create 的语义边界。错误类型看似简单,但跨平台和嵌套错误包装会让判断逻辑迅速变脆。