grpc/grpc-go · error
authz: authorization policy file path is empty
Error message
authz: authorization policy file path is empty
What it means
Returned by authz.NewFileWatcherWithOptions (grpc_authz_server_interceptors.go:137) when FileWatcherOptions.PolicyFile is the empty string. The FileWatcher interceptor needs a file path to read the JSON authorization policy from on startup and on each refresh tick, so an empty path is rejected immediately and no watcher goroutine is started.
Solutions
- Provide a non-empty policy file path in FileWatcherOptions.PolicyFile (or the first arg to NewFileWatcher).
- Resolve the path from config/flag/env and fail application startup early if it is unset, before constructing the interceptor.
- If you do not want file-based policy, use authz.NewStatic(policyJSON) instead of the file watcher.
Example fix
// before
fw, err := authz.NewFileWatcher(policyPath, 10*time.Second)
// after
if policyPath == "" {
log.Fatal("AUTHZ_POLICY_FILE is required")
}
fw, err := authz.NewFileWatcher(policyPath, 10*time.Second) Defensive patterns
Strategy: validation
Validate before calling
opts := authz.FileWatcherOptions{PolicyFile: policyPath, RefreshDuration: refresh}
if opts.PolicyFile == "" {
log.Fatal("authz: policy file path must be set")
}
fw, err := authz.NewFileWatcherWithOptions(opts) Try / catch
fw, err := authz.NewFileWatcherWithOptions(opts)
if err != nil {
if strings.Contains(err.Error(), "policy file path is empty") {
// supply PolicyFile and reconstruct
}
} Prevention
- Fail fast at startup if the policy path env/flag is empty.
- Prefer absolute paths for the policy file.
- Use NewStatic if you do not need file-based reload.
When it happens
Trigger: Calling NewFileWatcher("") or NewFileWatcherWithOptions with an unset PolicyFile field; reading the path from an env var or flag that defaulted to empty; constructing the interceptor before configuration loading completes.
Common situations: Flag/env-driven config where the policy path was not supplied in a given environment; wiring code that builds the interceptor unconditionally and supplies the path later; silent default of an empty string.
Related errors
- "allow_rules" is not present
- "allow_rules
- authz: requires refresh interval
- : "name" is not present
- %d: %v
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/d2c730139f8e9227.
Report an issue: GitHub.
Appendix: source
Thrown at authz/grpc_authz_server_interceptors.go:137
}
// NewFileWatcher returns a new FileWatcherInterceptor from a policy file
// that contains JSON string of authorization policy and a refresh duration to
// specify the amount of time between policy refreshes.
func NewFileWatcher(file string, duration time.Duration) (*FileWatcherInterceptor, error) {
return NewFileWatcherWithOptions(FileWatcherOptions{PolicyFile: file, RefreshDuration: duration, OnPolicyUpdate: nil})
}
// NewFileWatcherWithOptions returns a new FileWatcherInterceptor from a set of
// options.
//
// # Experimental
//
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
// later release.
func NewFileWatcherWithOptions(options FileWatcherOptions) (*FileWatcherInterceptor, error) {
if options.PolicyFile == "" {
return nil, fmt.Errorf("authz: authorization policy file path is empty")
}
if options.RefreshDuration <= time.Duration(0) {
return nil, fmt.Errorf("authz: requires refresh interval(%v) greater than 0s", options.RefreshDuration)
}
i := &FileWatcherInterceptor{options: options}
if err := i.updateInternalInterceptor(); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
i.cancel = cancel
// Create a background go routine for policy refresh.
go i.run(ctx)
return i, nil
}
func (i *FileWatcherInterceptor) run(ctx context.Context) {
ticker := time.NewTicker(i.options.RefreshDuration)
for {View on GitHub (pinned to 0c51461d27)