{"record":{"id":"b98af71bc07d9263","repo":"crowdsecurity/crowdsec","slug":"could-not-access-crl-file-w","errorCode":null,"errorMessage":"could not access CRL file: %w","messagePattern":"could not access CRL file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/apiserver/middlewares/v1/crl.go","lineNumber":76,"sourceCode":"\n\treturn crls, nil\n}\n\n// refresh() reads the CRL file if new or changed since the last time\nfunc (cc *CRLChecker) refresh() error {\n\t// noop if lastLoad is less than 5 seconds ago\n\tif time.Since(cc.lastLoad) < 5*time.Second {\n\t\treturn nil\n\t}\n\n\tcc.mu.Lock()\n\tdefer cc.mu.Unlock()\n\n\tcc.logger.Debugf(\"loading CRL file from %s\", cc.path)\n\n\tfileInfo, err := os.Stat(cc.path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not access CRL file: %w\", err)\n\t}\n\n\t// noop if the file didn't change\n\tif cc.fileInfo != nil && fileInfo.ModTime().Equal(cc.fileInfo.ModTime()) && fileInfo.Size() == cc.fileInfo.Size() {\n\t\treturn nil\n\t}\n\n\t// the encoding/pem package wants bytes, not io.Reader\n\tcrlContent, err := os.ReadFile(cc.path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not read CRL file: %w\", err)\n\t}\n\n\tcc.crls, err = cc.decodeCRLs(crlContent)\n\tif err != nil {\n\t\treturn err\n\t}\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/middlewares/v1/crl.go#L58-L94","documentation":"refresh() cannot stat the configured CRL file (os.Stat error) when loading or re-checking revocation lists for LAPI TLS client auth. The checker needs to know the file's mtime/size to skip redundant reloads, so an unreadable path is a hard error.","triggerScenarios":"NewCRLChecker or isRevokedBy -> refresh runs while cc.path (api.crl_path in LAPI TLS config) does not exist, has wrong permissions, or the mount is unavailable; os.Stat returns *PathError and it is wrapped as 'could not access CRL file'.","commonSituations":"crl_path typo in config.yaml; CRL file deleted by CA rotation before crowdsec restart; permissions changed so the crowdsec user cannot read the directory; Docker volume not mounted.","solutions":["Verify the file exists and is readable: ls -l <crl_path>; fix the path in the 'api.server.tls.crl_path' config key","Recreate/download the CRL file from your CA","Fix permissions/ownership so the crowdsec process user can read it","If running in a container, mount the CRL (and its directory) into the container and check for stale volume paths"],"exampleFix":"// before (config.yaml)\napi:\n  server:\n    tls:\n      crl_path: /etc/crowdsec/ssl/crl.pem.bak\n// after\napi:\n  server:\n    tls:\n      crl_path: /etc/crowdsec/ssl/crl.pem","handlingStrategy":"validation","validationCode":"// check before configuring/starting\nif _, err := os.Stat(crlPath); err != nil {\n    return fmt.Errorf(\"CRL path %s is not accessible: %w\", crlPath, err)\n}","typeGuard":"func crlFileAccessible(path string) bool {\n    fi, err := os.Stat(path)\n    return err == nil && !fi.IsDir()\n}","tryCatchPattern":"if err := checker.Refresh(); err != nil {\n    if strings.Contains(err.Error(), \"could not access CRL file\") {\n        log.Errorf(\"CRL missing at %s, revocation checks will fail: %v\", crlPath, err)\n    }\n}","preventionTips":["Verify crl_path exists and is readable by the crowdsec user before starting LAPI","In containers, explicitly mount the CRL file or its directory","Ensure CA rotation never deletes the CRL before crowdsec has re-read it","Add a health check that stats the CRL path and alerts on disappearance"],"tags":["tls","crl","filesystem","config","go"],"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-14T00:17:10.932Z"}