{"record":{"id":"b4c63f1174f0b9f8","repo":"thanos-io/thanos","slug":"adding-path-s-to-file-watcher","errorCode":null,"errorMessage":"adding path %s to file watcher","messagePattern":"adding path (.+?) to file watcher","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/receive/config.go","lineNumber":193,"sourceCode":"\thashringNodesGauge   *prometheus.GaugeVec\n\thashringTenantsGauge *prometheus.GaugeVec\n\n\t// lastLoadedConfigHash is the hash of the last successfully loaded configuration.\n\tlastLoadedConfigHash float64\n}\n\n// NewConfigWatcher creates a new ConfigWatcher.\nfunc NewConfigWatcher(logger log.Logger, reg prometheus.Registerer, path string, interval model.Duration) (*ConfigWatcher, error) {\n\tif logger == nil {\n\t\tlogger = log.NewNopLogger()\n\t}\n\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"creating file watcher\")\n\t}\n\tif err := watcher.Add(path); err != nil {\n\t\treturn nil, errors.Wrapf(err, \"adding path %s to file watcher\", path)\n\t}\n\n\tc := &ConfigWatcher{\n\t\tch:       make(chan []HashringConfig),\n\t\tpath:     path,\n\t\tinterval: time.Duration(interval),\n\t\tlogger:   logger,\n\t\twatcher:  watcher,\n\t\thashGauge: promauto.With(reg).NewGauge(\n\t\t\tprometheus.GaugeOpts{\n\t\t\t\tName: \"thanos_receive_config_hash\",\n\t\t\t\tHelp: \"Hash of the currently loaded hashring configuration file.\",\n\t\t\t}),\n\t\tsuccessGauge: promauto.With(reg).NewGauge(\n\t\t\tprometheus.GaugeOpts{\n\t\t\t\tName: \"thanos_receive_config_last_reload_successful\",\n\t\t\t\tHelp: \"Whether the last hashring configuration file reload attempt was successful.\",\n\t\t\t}),","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/receive/config.go#L175-L211","documentation":"NewConfigWatcher wraps a failure from watcher.Add(path) when the OS refuses to watch the given hashring config path. fsnotify can only watch files that exist and are accessible to the process, so this error means the configured path is missing, is not a regular file, or is not readable.","triggerScenarios":"NewConfigWatcher called with a path that does not exist yet, points to a directory without permission, or was deleted between existence check and Add (Kubernetes symlink swap races).","commonSituations":"Wrong --hashrings-file path or typo; ConfigMap volume not mounted before startup; file created by a sidecar after Receive starts; permission mismatch between the mount and the running user.","solutions":["Verify the path exists and is readable by the Receive process user before startup (ls -l / test -r)","Fix the --hashrings-file flag value or the volume mount so the file is present at startup","Add an init container or entrypoint wait-loop that blocks until the config file exists","Check mount permissions (readOnly ConfigMap mounts owned by root vs non-root process)"],"exampleFix":"// before (file may not exist)\nwatcher, _ := receive.NewConfigWatcher(logger, cfgPath, interval)\n// after\nfor {\n\tif _, err := os.Stat(cfgPath); err == nil { break }\n\ttime.Sleep(time.Second)\n}\nwatcher, err := receive.NewConfigWatcher(logger, cfgPath, interval)","handlingStrategy":"validation","validationCode":"info, err := os.Stat(path)\nif err != nil { return fmt.Errorf(\"hashring file missing: %w\", err) }\nif info.IsDir() { return errors.New(\"path is a directory, not a file\") }\nf, err := os.Open(path)\nif err != nil { return fmt.Errorf(\"not readable: %w\", err) }\nf.Close()","typeGuard":null,"tryCatchPattern":"w, err := receive.NewConfigWatcher(logger, path, interval)\nif err != nil && strings.Contains(err.Error(), \"adding path\") {\n\t// wait for the file to appear / fix mount, then retry\n}","preventionTips":["Use an init container or entrypoint wait-loop for the config mount","Verify the exact flag path matches the mounted file","Mount ConfigMaps read-only with correct ownership for the run user"],"tags":["fsnotify","file-not-found","config"],"backgroundTag":"file-not-found","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}