{"record":{"id":"bd711cd84488ac5d","repo":"grpc/grpc-go","slug":"policyfile-s-read-failed-v","errorCode":null,"errorMessage":"policyFile(%s) read failed: %v","messagePattern":"policyFile\\((.+?)\\) read failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"authz/grpc_authz_server_interceptors.go","lineNumber":175,"sourceCode":"\t\t\tlogger.Warningf(\"authorization policy reload status err: %v\", err)\n\t\t}\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tticker.Stop()\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t}\n\t}\n}\n\n// updateInternalInterceptor checks if the policy file that is watching has changed,\n// and if so, updates the internalInterceptor with the policy. Unlike the\n// constructor, if there is an error in reading the file or parsing the policy, the\n// previous internalInterceptors will not be replaced.\nfunc (i *FileWatcherInterceptor) updateInternalInterceptor() error {\n\tpolicyContents, err := os.ReadFile(i.options.PolicyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"policyFile(%s) read failed: %v\", i.options.PolicyFile, err)\n\t}\n\tif bytes.Equal(i.policyContents, policyContents) {\n\t\treturn nil\n\t}\n\ti.policyContents = policyContents\n\tpolicyContentsString := string(policyContents)\n\tinterceptor, err := NewStatic(policyContentsString)\n\tif err != nil {\n\t\treturn err\n\t}\n\tatomic.StorePointer(&i.internalInterceptor, unsafe.Pointer(interceptor))\n\tlogger.Infof(\"authorization policy reload status: successfully loaded new policy %v\", policyContentsString)\n\tif i.options.OnPolicyUpdate != nil {\n\t\ti.options.OnPolicyUpdate(policyContentsString)\n\t}\n\treturn nil\n}\n","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/authz/grpc_authz_server_interceptors.go#L157-L193","documentation":"Returned by FileWatcherInterceptor.updateInternalInterceptor (grpc_authz_server_interceptors.go:175) when os.ReadFile(options.PolicyFile) fails; the underlying error is wrapped with the file path. This is invoked both at construction time (NewFileWatcherWithOptions) and on every refresh tick (run goroutine). On refresh failure the previously loaded interceptor is kept, so authorization continues with the last good policy; at construction time the error propagates to the caller and the watcher is not started.","triggerScenarios":"Policy file does not exist, is not readable (permissions), path is a directory, or the path is on a filesystem that is temporarily unavailable; also symlink breakage or container volume mount not yet ready at startup.","commonSituations":"Wrong/relative path in a container where CWD differs; ConfigMap/secret not mounted yet when the process starts; file replaced atomically with a brief window of ENOENT; permission mismatch (mode 0600 owned by another uid); refresh interval firing during a deploy that swaps the file.","solutions":["Confirm the path is absolute and the file exists and is readable by the process uid (ls -l, stat).","Ensure mounted config (Kubernetes ConfigMap/secret, volume) is available before app start; use a readiness check or retry-on-startup.","After fixing, let the file watcher self-heal on the next tick (it logs a warning and keeps the old policy); only a construction-time failure blocks startup.","If using a relative path, switch to an absolute path or resolve relative to the config directory explicitly."],"exampleFix":"// before\nfw, err := authz.NewFileWatcher(\"policy.json\", 10*time.Second) // CWD-dependent\n\n// after\nfw, err := authz.NewFileWatcher(\"/etc/app/authz/policy.json\", 10*time.Second)","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(policyPath); err != nil {\n    log.Fatalf(\"authz policy file not accessible: %v\", err)\n}\nfw, err := authz.NewFileWatcher(policyPath, refresh)","typeGuard":null,"tryCatchPattern":"fw, err := authz.NewFileWatcher(policyPath, refresh)\nif err != nil {\n    if strings.Contains(err.Error(), \"read failed\") {\n        // check existence/permissions; the running watcher keeps the prior policy on refresh errors\n    }\n}","preventionTips":["Use absolute paths; confirm the file exists and is readable by the process uid.","Ensure mounted config (ConfigMap/secret) is present before process start.","Rely on the file watcher's self-healing: refresh errors keep the last good policy."],"tags":["grpc","authz","filesystem","config","go"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}