{"record":{"id":"2973f75250e3c0b4","repo":"prometheus/node_exporter","slug":"failed-to-open-textfile-data-file-q-w","errorCode":null,"errorMessage":"failed to open textfile data file %q: %w","messagePattern":"failed to open textfile data file %q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"collector/textfile.go","lineNumber":292,"sourceCode":"\n\tch <- prometheus.MustNewConstMetric(\n\t\tprometheus.NewDesc(\n\t\t\t\"node_textfile_scrape_error\",\n\t\t\t\"1 if there was an error opening or reading a file, 0 otherwise\",\n\t\t\tnil, nil,\n\t\t),\n\t\tprometheus.GaugeValue, errVal,\n\t)\n\n\treturn nil\n}\n\n// processFile processes a single file, returning its modification time on success.\nfunc (c *textFileCollector) processFile(dir, name string) (*time.Time, map[string]*dto.MetricFamily, error) {\n\tpath := filepath.Join(dir, name)\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to open textfile data file %q: %w\", path, err)\n\t}\n\tdefer f.Close()\n\n\tparser := expfmt.NewTextParser(model.UTF8Validation)\n\tfamilies, err := parser.TextToMetricFamilies(f)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to parse textfile data from %q: %w\", path, err)\n\t}\n\n\tif hasTimestamps(families) {\n\t\treturn nil, nil, fmt.Errorf(\"textfile %q contains unsupported client-side timestamps, skipping entire file\", path)\n\t}\n\n\t// Only stat the file once it has been parsed and validated, so that\n\t// a failure does not appear fresh.\n\tstat, err := f.Stat()\n\tif err != nil {\n\t\treturn nil, families, fmt.Errorf(\"failed to stat %q: %w\", path, err)","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/prometheus/node_exporter/blob/17ddd77c59ba27e1508e9f7894b1e55b44d6aed3/collector/textfile.go#L274-L310","documentation":"textFileCollector.processFile opens each file in the textfile directory with os.Open; any open failure (missing file, permission denied, path is a directory) is wrapped as this error. node_exporter's textfile collector reads Prometheus exposition-format files dropped by other processes and merges them into the scrape.","triggerScenarios":"A file listed/visible in the textfile directory disappears between readdir and open (writer removing files); the file is a directory; the exporter user lacks read permission; stale symlink targets.","commonSituations":"Cron jobs that `mv` or delete .prom files while a scrape is in flight; files written with root-only permissions while node_exporter runs as the prometheus user; misconfigured directory containing subdirectories; broken symlinks left by deploy scripts.","solutions":["Check file permissions: ensure the user node_exporter runs as can read the files (chmod 644, correct group)","Make writers atomic: write to a temp file and rename() into place instead of deleting/recreating during scrape","Ensure the textfile directory (--collector.textfile.directory) contains only regular readable files, not subdirectories","If the wrapped cause is permission denied, adjust SELinux/AppArmor or directory ownership"],"exampleFix":"// before (writer): non-atomic delete during scrape\nrm /var/lib/node_exporter/textfile/foo.prom && write foo.prom\n// after (writer): atomic replace\ntmp=$(mktemp /var/lib/node_exporter/textfile/foo.prom.XXXXXX)\nwrite \"$tmp\" && chmod 644 \"$tmp\" && mv \"$tmp\" /var/lib/node_exporter/textfile/foo.prom","handlingStrategy":"validation","validationCode":"files, _ := os.ReadDir(textfileDir)\nfor _, f := range files {\n    if !f.Type().IsRegular() { continue }\n    if fh, err := os.Open(filepath.Join(textfileDir, f.Name())); err != nil {\n        log.Warn(\"unreadable textfile\", \"name\", f.Name(), \"err\", err)\n    } else { fh.Close() }\n}","typeGuard":null,"tryCatchPattern":"if err := coll.Update(ch); err != nil {\n    log.Warn(\"textfile scrape issue\", \"err\", err) // surfaced via node_exporter textfile scrape error metric\n}","preventionTips":["Write .prom files atomically (temp + rename)","Set directory/file ownership so the exporter user can read them","Keep only regular files in the textfile directory — no subdirs or symlinks","Run as a dedicated user with a managed textfile directory"],"tags":["textfile","filesystem","linux"],"backgroundTag":"file-open-failed","analyzedSha":"17ddd77c59ba27e1508e9f7894b1e55b44d6aed3","analyzedAt":"2026-09-07T17:54:06.211Z","contentChangedAt":"2026-09-07T17:54:06.211Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}