{"record":{"id":"dde298f6bb83e565","repo":"cilium/cilium","slug":"cannot-read-file-s-w","errorCode":null,"errorMessage":"cannot read file '%s': %w","messagePattern":"cannot read file '(.+?)': %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hubble/metrics/metric_config_watcher.go","lineNumber":110,"sourceCode":"\tc.configFilePath = path\n}\n\n// Stop stops watcher.\nfunc (c *metricConfigWatcher) Stop() {\n\tif c.ticker != nil {\n\t\tc.ticker.Stop()\n\t}\n\tclose(c.stop)\n}\n\nfunc (c *metricConfigWatcher) readConfig() (*api.Config, bool, uint64, error) {\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\n\tconfig := &api.Config{Metrics: []*api.MetricConfig{}}\n\tyamlFile, err := os.ReadFile(c.configFilePath)\n\tif err != nil {\n\t\treturn nil, false, 0, fmt.Errorf(\"cannot read file '%s': %w\", c.configFilePath, err)\n\t}\n\tif err := yaml.Unmarshal(yamlFile, config); err != nil {\n\t\treturn nil, false, 0, fmt.Errorf(\"cannot parse yaml: %w\", err)\n\t}\n\n\tif err := c.validateMetricConfig(config); err != nil {\n\t\treturn nil, false, 0, fmt.Errorf(\"invalid yaml config file: %w\", err)\n\t}\n\n\tnewHash := calculateMetricHash(yamlFile)\n\tisSameHash := newHash == c.currentCfgHash\n\tc.currentCfgHash = newHash\n\n\treturn config, isSameHash, newHash, nil\n}\n\nfunc calculateMetricHash(file []byte) uint64 {\n\tsum := md5.Sum(file)","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/hubble/metrics/metric_config_watcher.go#L92-L128","documentation":"The Hubble metric ConfigMap watcher's readConfig could not read the YAML config file at c.configFilePath (os.ReadFile failed) and wraps the OS error. This means the path is missing, unreadable, or is a directory — Hubble then keeps the previous metric configuration. The wrapped error (e.g., 'no such file or directory', 'permission denied') tells the exact cause.","triggerScenarios":"ConfigMap volume not yet mounted (empty/cleaned mount during kubelet refresh); wrong --hubble-metrics-config-file path; file deleted between mounts; permission changes on the mounted file.","commonSituations":"Kubernetes ConfigMap updates with atomic mounts causing transient ENOENT; typo'd CLI flag; running hubble with a mounted ConfigMap removed from the pod spec.","solutions":["Verify the --hubble-metrics-config-file path exists and is readable by the hubble process","Check the ConfigMap volume is mounted in the pod (kubectl describe pod)","If transient during ConfigMap update, rely on the watcher retaining the last good config and retrying on the next fs event","Fix file permissions/ownership on the mounted path"],"exampleFix":"// before\nhubble:\n  metrics-config-file: /etc/hubble/config.yaml  # file not mounted\n// after\n# mount the ConfigMap and point the flag at it:\n# --hubble-metrics-config-file=/etc/hubble-metrics/config.yaml\nvolumeMounts:\n- name: hubble-metrics\n  mountPath: /etc/hubble-metrics","handlingStrategy":"validation","validationCode":"// before relying on the watcher, ensure the file exists and is readable:\nimport \"os\"\nfunc configReadable(path string) error {\n    fi, err := os.Stat(path)\n    if err != nil { return err }\n    if fi.IsDir() { return fmt.Errorf(\"%s is a directory\", path) }\n    f, err := os.Open(path)\n    if err != nil { return err }\n    return f.Close()\n}","typeGuard":null,"tryCatchPattern":"// watcher already keeps last-good config; on reload error:\ncfg, changed, hash, err := c.readConfig()\nif err != nil {\n    log.WithError(err).Warn(\"keeping previous hubble metric config\")\n    return\n}","preventionTips":["Verify the --hubble-metrics-config-file path matches the volumeMount","Ensure the ConfigMap is mounted before hubble starts (initContainer wait or retry)","Watch for kubelet atomic-mount windows that briefly remove the file","Check file permissions for the hubble user"],"tags":["file-io","config","hubble","kubernetes"],"backgroundTag":"config-file-not-found","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}