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

  1. Ensure the process has search (x) permission on every directory component of the validated path.
  2. Pre-check the path with your own os.Stat and classify errors (errors.Is(err, fs.ErrNotExist) vs others) before validation.
  3. Wrap Validate calls in recover() to convert the panic into an error for paths you cannot control.
  4. 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

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


AI-assisted analysis of go-playground/validator@facf128d2e (2026-09-02). Data as JSON: /api/errors/cc10ab788cc7d988. Report an issue: GitHub.