crowdsecurity/crowdsec · error

could not parse file: %w

Error message

could not parse file: %w

What it means

decodeCRLs fails when a PEM block found in the CRL file is not a valid x509 revocation list (x509.ParseRevocationList errors). To avoid serving a truncated/corrupt CRL, the whole file is invalidated and the previous CRL version keeps being used.

Source

Thrown at pkg/apiserver/middlewares/v1/crl.go:53

	return cc, nil
}

func (*CRLChecker) decodeCRLs(content []byte) ([]*x509.RevocationList, error) {
	var crls []*x509.RevocationList

	for {
		block, rest := pem.Decode(content)
		if block == nil {
			break // no more PEM blocks
		}

		content = rest

		crl, err := x509.ParseRevocationList(block.Bytes)
		if err != nil {
			// invalidate the whole CRL file so we can still use the previous version
			return nil, fmt.Errorf("could not parse file: %w", err)
		}

		crls = append(crls, crl)
	}

	return crls, nil
}

// refresh() reads the CRL file if new or changed since the last time
func (cc *CRLChecker) refresh() error {
	// noop if lastLoad is less than 5 seconds ago
	if time.Since(cc.lastLoad) < 5*time.Second {
		return nil
	}

	cc.mu.Lock()
	defer cc.mu.Unlock()

View on GitHub (pinned to 909b515798)

Solutions

  1. Regenerate/redownload the CRL file from the CA in a valid PEM DER format (openssl ca -gencrl / openssl crl -in crl.der -out crl.pem)
  2. Check the file contains only PEM 'X509 CRL' blocks: openssl crl -in <file> -noout -text
  3. Fix the process writing the CRL so it writes atomically (write temp file + rename) instead of truncating in place
  4. Verify the LAPI api.crl_path configuration points to the actual CRL file, not a certificate

Example fix

// before: non-atomic CRL update by cron
wget -O /etc/crowdsec/ssl/crl.pem http://ca/crl.pem
// after: atomic replace
echo 'X509 CRL' >/dev/null; wget -O /etc/crowdsec/ssl/crl.pem.tmp http://ca/crl.pem && mv /etc/crowdsec/ssl/crl.pem.tmp /etc/crowdsec/ssl/crl.pem
Defensive patterns

Strategy: fallback

Validate before calling

// validate the CRL PEM before pointing crowdsec at it
b, _ := os.ReadFile(crlPath)
block, _ := pem.Decode(b)
if block == nil || _, err := x509.ParseRevocationList(block.Bytes); err != nil {
    return fmt.Errorf("invalid CRL file %s: %w", crlPath, err)
}

Type guard

func isValidPEMCRL(data []byte) bool {
    block, _ := pem.Decode(data)
    if block == nil {
        return false
    }
    _, err := x509.ParseRevocationList(block.Bytes)
    return err == nil
}

Try / catch

if err := checker.Refresh(); err != nil {
    if strings.Contains(err.Error(), "could not parse file") {
        log.Warnf("CRL file invalid, keeping previous version: %v", err)
    }
}

Prevention

When it happens

Trigger: refresh() reads the CRL file (after detecting a modtime/size change), passes bytes to decodeCRLs; a PEM block's DER payload fails x509.ParseRevocationList -> 'could not parse file: %w'. Caused by corrupt downloads, wrong PEM type (e.g. a certificate pasted in the CRL file), or an old CRL format.

Common situations: CA renewal scripts write partial/empty CRL files; user concatenates cert+CRL; TLS CRL generated with an outdated tool producing a v1 list; file truncated mid-download.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/467babc46530b30a. Report an issue: GitHub.