{"record":{"id":"cc10ab788cc7d988","repo":"go-playground/validator","slug":"panic-err-os-stat-patherror-re-raised-for-unexp","errorCode":null,"errorMessage":"panic(err) (os.Stat *PathError re-raised for unexpected stat failure)","messagePattern":"panic\\(err\\) \\(os\\.Stat \\*PathError re-raised for unexpected stat failure\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"baked_in.go","lineNumber":1894,"sourceCode":"\t\t\treturn false\n\t\t}\n\t\tif _, err = os.Stat(field.String()); err != nil {\n\t\t\tswitch t := err.(type) {\n\t\t\tcase *fs.PathError:\n\t\t\t\tif t.Err == syscall.EINVAL {\n\t\t\t\t\t// It's definitely an invalid character in the filepath.\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\t// It could be a permission error, a does-not-exist error, etc.\n\t\t\t\t// Out-of-scope for this validation, though.\n\t\t\t\treturn true\n\t\t\tdefault:\n\t\t\t\t// Something went *seriously* wrong.\n\t\t\t\t/*\n\t\t\t\t\tPer https://pkg.go.dev/os#Stat:\n\t\t\t\t\t\t\"If there is an error, it will be of type *PathError.\"\n\t\t\t\t*/\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t}\n\t}\n\n\tpanic(fmt.Sprintf(\"Bad field type %s\", field.Type()))\n}\n\n// isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.\nfunc isE164(fl FieldLevel) bool {\n\treturn e164Regex().MatchString(fl.Field().String())\n}\n\n// isEmail is the validation function for validating if the current field's value is a valid email address.\nfunc isEmail(fl FieldLevel) bool {\n\t_, err := mail.ParseAddress(fl.Field().String())\n\tif err != nil {\n\t\treturn false\n\t}","sourceCodeStart":1876,"sourceCodeEnd":1912,"githubUrl":"https://github.com/go-playground/validator/blob/facf128d2eecf42bd4ff447adc961aa21bb64aed/baked_in.go#L1876-L1912","documentation":"This panic is re-raised inside a file-path validator's error handling: when os.Stat fails with an error that is NOT os.ErrNotExist (e.g. permission denied, I/O error, too many symlinks), the code considers it a serious unexpected condition and panics with the underlying *fs.PathError rather than silently returning false. Only the not-exist case maps to a normal validation failure.","triggerScenarios":"Validating a path whose parent directory is not searchable (chmod 000), stat-ing a path on a detached/failing mount, hitting ELOOP, EACCES from missing traverse permissions, or stat-ing a file while the filesystem returns EIO. Any Stat error other than ErrNotExist in the tagged field's path triggers the re-panic.","commonSituations":"Containers running as non-root validating paths owned by other users; NFS/EFS or FUSE mounts flaking at request time; validating paths under directories with restrictive permissions after a deployment change; macOS/Windows permission differences in CI.","solutions":["Ensure the process has search (x) permission on every directory component of the validated path.","Pre-check the path with your own os.Stat and classify errors (errors.Is(err, fs.ErrNotExist) vs others) before validation.","Wrap Validate calls in recover() to convert the panic into an error for paths you cannot control.","Avoid validating paths on network/unreliable mounts on the hot path; copy or probe them separately."],"exampleFix":"// before\nv.Struct(cfg) // panics on EACCES from os.Stat inside the `file` validator\n\n// after\nfunc validatePathSafe(v *validator.Validate, s any) (err error) {\n\tdefer func() { if r := recover(); r != nil { err = fmt.Errorf(\"validation panicked: %v\", r) } }()\n\treturn v.Struct(s)\n}","handlingStrategy":"try-catch","validationCode":"// Pre-classify Stat errors before validating:\nif _, err := os.Stat(path); err != nil {\n\tswitch {\n\tcase errors.Is(err, fs.ErrNotExist):\n\t\t// normal validation failure path\n\tcase errors.Is(err, fs.ErrPermission):\n\t\treturn fmt.Errorf(\"no permission to stat %s: %w\", path, err)\n\tdefault:\n\t\treturn fmt.Errorf(\"unexpected stat failure on %s: %w\", path, err)\n\t}\n}","typeGuard":"func statSafe(path string) (fs.FileInfo, error) {\n\tinfo, err := os.Stat(path)\n\tif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"unexpected stat error: %w\", err)\n\t}\n\treturn info, err\n}","tryCatchPattern":"func validateStructSafe(v *validator.Validate, s any) (err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"validation panicked (check path permissions/mounts): %v\", r)\n\t\t}\n\t}()\n\treturn v.Struct(s)\n}","preventionTips":["Ensure the validating process has execute/search permission on all parent directories of validated paths","Do not run file-existence validation against flaky network mounts on request hot paths","Pre-stat and classify errors yourself so only ErrNotExist reaches the validator","Wrap Validate calls in a recover helper in services validating user-supplied paths"],"tags":["go","validator","panic","filesystem","os-stat","permissions","patherror"],"backgroundTag":"os-stat-permission-denied","analyzedSha":"facf128d2eecf42bd4ff447adc961aa21bb64aed","analyzedAt":"2026-09-02T10:19:04.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T16:17:10.729Z"}