{"record":{"id":"b4d98b3fffc90055","repo":"crowdsecurity/crowdsec","slug":"unable-to-read-s-s","errorCode":null,"errorMessage":"unable to read %s : %s","messagePattern":"unable to read (.+?) : (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/acquisition/modules/file/run.go","lineNumber":213,"sourceCode":"\t\treturn nil\n\t}\n\n\t// Check if we're already tailing\n\ts.tailMapMutex.RLock()\n\n\tif s.tails[file] {\n\t\ts.tailMapMutex.RUnlock()\n\t\tlogger.Debugf(\"Already tailing file %s, not creating a new tail\", file)\n\n\t\treturn nil\n\t}\n\n\ts.tailMapMutex.RUnlock()\n\n\t// Validate file\n\tfd, err := os.Open(file)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to read %s : %s\", file, err)\n\t}\n\n\tif err = fd.Close(); err != nil {\n\t\treturn fmt.Errorf(\"unable to close %s : %s\", file, err)\n\t}\n\n\tfi, err := os.Stat(file)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not stat file %s : %w\", file, err)\n\t}\n\n\tif fi.IsDir() {\n\t\tlogger.Warnf(\"%s is a directory, ignoring it.\", file)\n\t\treturn nil\n\t}\n\n\t// Determine polling mode\n\tpollFile := false","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/acquisition/modules/file/run.go#L195-L231","documentation":"CrowdSec's file streaming acquisition calls setupTailForFile to begin tailing a log file. Before setting up the tail, it opens the file with os.Open to verify it is readable; if that open fails, it wraps the OS error in \"unable to read %s : %s\". It means the acquisition module cannot access the file at all (it doesn't exist, permissions deny, or path issues).","triggerScenarios":"os.Open(file) returns an error during setupTailForFile, called from StreamingAcquisition startup or checkAndTailFile when a new file appears matching the acquisition pattern.","commonSituations":"Log file doesn't exist yet at startup; the crowdsec process runs as a non-root user lacking read permission; path typos in acquis.yaml; the file was rotated/deleted between glob match and open.","solutions":["Verify the file path in the acquisition config matches an existing file (ls the exact path).","Check read permissions for the crowdsec user on the file and its parent directories.","If the file may not exist yet, rely on the glob/pattern matching and ensure the producer creates it, or configure the module to tolerate missing files.","Run crowdsec in the foreground with verbose logging to see the underlying OS error (ENOENT vs EACCES)."],"exampleFix":"// before\ncat /var/log/missing.log  # No such file\n# after\nls -l /var/log/myapp.log && sudo usermod -aG adm crowdsec  # grant group read","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfunction canReadTailFile(path) {\n  try { fs.accessSync(path, fs.constants.R_OK); return true; } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-check existence/readability of glob targets before starting acquisition.","Run crowdsec as a user with group membership in the log files' group (e.g. adm).","Use glob patterns so acquisition picks up files as soon as they're created.","Test acquisition config with cscli before restarting the service."],"tags":["file-io","acquisition","go","permissions"],"backgroundTag":"file-not-found","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}