go-playground/validator · error
panic(err) (os.Stat *PathError re-raised for unexpected stat
Error message
panic(err) (os.Stat *PathError re-raised for unexpected stat failure)
What it means
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.
Source
Thrown at baked_in.go:1894
return false
}
if _, err = os.Stat(field.String()); err != nil {
switch t := err.(type) {
case *fs.PathError:
if t.Err == syscall.EINVAL {
// It's definitely an invalid character in the filepath.
return false
}
// It could be a permission error, a does-not-exist error, etc.
// Out-of-scope for this validation, though.
return true
default:
// Something went *seriously* wrong.
/*
Per https://pkg.go.dev/os#Stat:
"If there is an error, it will be of type *PathError."
*/
panic(err)
}
}
}
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
func isE164(fl FieldLevel) bool {
return e164Regex().MatchString(fl.Field().String())
}
// isEmail is the validation function for validating if the current field's value is a valid email address.
func isEmail(fl FieldLevel) bool {
_, err := mail.ParseAddress(fl.Field().String())
if err != nil {
return false
}View on GitHub (pinned to facf128d2e)
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.
Example fix
// before
v.Struct(cfg) // panics on EACCES from os.Stat inside the `file` validator
// after
func validatePathSafe(v *validator.Validate, s any) (err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("validation panicked: %v", r) } }()
return v.Struct(s)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-classify Stat errors before validating:
if _, err := os.Stat(path); err != nil {
switch {
case errors.Is(err, fs.ErrNotExist):
// normal validation failure path
case errors.Is(err, fs.ErrPermission):
return fmt.Errorf("no permission to stat %s: %w", path, err)
default:
return fmt.Errorf("unexpected stat failure on %s: %w", path, err)
}
} Type guard
func statSafe(path string) (fs.FileInfo, error) {
info, err := os.Stat(path)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("unexpected stat error: %w", err)
}
return info, err
} Try / catch
func validateStructSafe(v *validator.Validate, s any) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("validation panicked (check path permissions/mounts): %v", r)
}
}()
return v.Struct(s)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Bad field type %T
- Bad param number for required_if %s
- Duplicate param %s for required_if %s
- Bad param number for excluded_if %s
- Bad param number for required_unless %s
AI-assisted analysis of go-playground/validator@facf128d2e (2026-09-02).
Data as JSON: /api/errors/cc10ab788cc7d988.
Report an issue: GitHub.