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 file-based authorization interceptor needs a policy file to read and watch, so an empty path is rejected up front rather than failing later.

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 03255a9237)

Solutions

  1. Provide a non-empty policy file path to NewFileWatcher / NewFileWatcherWithOptions.
  2. Read the path from an env var or flag and fail fast at startup if it is empty.
  3. If authz is optional, skip constructing the interceptor entirely when no path is configured instead of passing "".
  4. Validate the path exists before passing it (also avoids the later read-failed error).

Example fix

// before
az, err := authz.NewFileWatcher("", 10*time.Second)

// after
path := os.Getenv("GRPC_AUTHZ_POLICY")
if path == "" {
    log.Fatal("GRPC_AUTHZ_POLICY must be set")
}
az, err := authz.NewFileWatcher(path, 10*time.Second)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(options.PolicyFile) == "" {
    return fmt.Errorf("authz policy file path is required")
}

Try / catch

az, err := authz.NewFileWatcherWithOptions(opts)
if err != nil {
    if strings.Contains(err.Error(), "policy file path is empty") {
        log.Fatal("set --authz-policy")
    }
}

Prevention

When it happens

Trigger: Calling NewFileWatcher("", duration) or NewFileWatcherWithOptions with an unset PolicyFile, e.g. when the path is supposed to come from an env var or config flag that was not provided.

Common situations: Forgetting to wire a --authz-policy flag; env var not set in the deployment; config struct field left as the zero value; conditional that should set the file but did not run.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/d2c730139f8e9227. Report an issue: GitHub.