crowdsecurity/crowdsec · error
could not read CRL file: %w
Error message
could not read CRL file: %w
What it means
refresh() fails reading the CRL file contents with os.ReadFile after a successful stat, typically a race (file replaced between stat and read) or an I/O/permission problem surfacing at read time. The error is wrapped as 'could not read CRL file'.
Source
Thrown at pkg/apiserver/middlewares/v1/crl.go:87
cc.mu.Lock()
defer cc.mu.Unlock()
cc.logger.Debugf("loading CRL file from %s", cc.path)
fileInfo, err := os.Stat(cc.path)
if err != nil {
return fmt.Errorf("could not access CRL file: %w", err)
}
// noop if the file didn't change
if cc.fileInfo != nil && fileInfo.ModTime().Equal(cc.fileInfo.ModTime()) && fileInfo.Size() == cc.fileInfo.Size() {
return nil
}
// the encoding/pem package wants bytes, not io.Reader
crlContent, err := os.ReadFile(cc.path)
if err != nil {
return fmt.Errorf("could not read CRL file: %w", err)
}
cc.crls, err = cc.decodeCRLs(crlContent)
if err != nil {
return err
}
cc.fileInfo = fileInfo
cc.lastLoad = time.Now()
cc.onLoad()
return nil
}
// isRevoked checks if the client certificate is revoked by any of the CRL blocks
// It returns a boolean indicating if the certificate is revoked and a boolean indicating
// if the CRL check was successful and could be cached.
func (cc *CRLChecker) isRevokedBy(cert *x509.Certificate, issuer *x509.Certificate) (bool, bool) {View on GitHub (pinned to 909b515798)
Solutions
- Ensure crl_path is a regular file readable by the crowdsec user (chmod/chown)
- Point crl_path at the stable symlink/file and have the writer do atomic rename, then retry
- Restart crowdsec after fixing permissions — refresh retries on the next isRevokedBy call
- Check filesystem health (dmesg, NFS mounts) if the error persists
Defensive patterns
Strategy: retry
Validate before calling
// check readability as the crowdsec user
f, err := os.Open(crlPath)
if err != nil {
return fmt.Errorf("cannot read CRL %s: %w", crlPath, err)
}
f.Close() Try / catch
err := checker.Refresh()
if err != nil && strings.Contains(err.Error(), "could not read CRL file") {
time.Sleep(100 * time.Millisecond)
err = checker.Refresh() // retry: often a race with atomic rename
} Prevention
- Run the CRL writer with atomic rename to avoid read-during-replace races
- Set permissions (chmod 644 or group-readable) matching the crowdsec service user
- Point crl_path at a stable symlink, not a path writers recreate
- Watch for NFS/network filesystem I/O errors on the CRL volume
When it happens
Trigger: NewCRLChecker/isRevokedBy -> refresh: os.Stat succeeded but os.ReadFile(cc.path) returns EACCES, EISDIR, EIO, or 'file changed as we read it' because the CA rotation replaced the path mid-read.
Common situations: crl_path points at a directory instead of a file; crowdsec runs as non-root while the file is 0600 root-owned; concurrent atomic rename racing the reader; disk/network filesystem (NFS) I/O errors.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- could not access CRL file: %w
- could not parse file: %w
- certificate revoked by CRL
- cannot use TLS with a unix socket
- user/password authentication and TLS authentication are mutu
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/f888328e5870db8b.
Report an issue: GitHub.