crowdsecurity/crowdsec · error
invalid rdns regex '%s' for bot entry '%s' in %s: %w
Error message
invalid rdns regex '%s' for bot entry '%s' in %s: %w
What it means
Each non-empty element of the "rdns" array is compiled as a case-insensitive regex matched against FCrDNS-verified hostnames. This error is thrown when the pattern is not valid Go RE2 syntax. Compilation happens at load time so bad patterns abort data-file initialization with the offending pattern, entry, and file named.
Source
Thrown at pkg/exprhelpers/botfile.go:112
for _, r := range entry.Ranges {
prefix, err := netip.ParsePrefix(r)
if err != nil {
return fmt.Errorf("invalid CIDR range '%s' for bot entry '%s' in %s: %w", r, entry.Name, filename, err)
}
entry.prefixes = append(entry.prefixes, prefix.Masked())
}
for _, p := range entry.RDNS {
// an empty pattern matches every PTR-confirmed host: almost
// certainly a mistake, reject it
if p == "" {
return fmt.Errorf("empty rdns pattern for bot entry '%s' in %s", entry.Name, filename)
}
re, err := compileBotRegex(p)
if err != nil {
return fmt.Errorf("invalid rdns regex '%s' for bot entry '%s' in %s: %w", p, entry.Name, filename, err)
}
entry.rdnsRegexes = append(entry.rdnsRegexes, re)
}
dataFileBots[filename] = append(dataFileBots[filename], entry)
return nil
}
// parseBotAddr normalizes a source address as found in HTTP contexts:
// bare IP, "ip:port", "[v6]:port". The zone is stripped and IPv4-mapped
// IPv6 is unmapped so comparisons against load-time-parsed IPs/ranges are
// consistent.
func parseBotAddr(s string) (netip.Addr, bool) {
addr, err := netip.ParseAddr(s)
if err != nil {
host, _, splitErr := net.SplitHostPort(s)View on GitHub (pinned to 909b515798)
Solutions
- Fix the syntax error indicated by the wrapped regexp/syntax message (it names the position).
- Convert globs to regex: *.googlebot.com becomes (^|\.)googlebot\.com$.
- Remove RE2-unsupported constructs (lookaheads (?=...), lookbehinds, backreferences \1) and restructure the pattern.
- Anchor the pattern — (^|\.)domain\.tld$ — so attacker-controlled subdomain suffixes cannot match.
- Test with Go: regexp.Compile("(?i)" + pattern) before editing the data file.
Example fix
// before
{"name":"gbot","rdns":["(?=.*google)\\1"]}
// after
{"name":"gbot","rdns":["(^|\.)googlebot\.com$"]} Defensive patterns
Strategy: validation
Validate before calling
for _, p := range entry.RDNS {
if _, err := regexp.Compile("(?i)" + p); err != nil {
// reject before FileInit
}
}
valid := err == nil Try / catch
if err := exprhelpers.FileInit(botFile, "bots"); err != nil {
if strings.Contains(err.Error(), "invalid rdns regex") {
log.Errorf("offending rdns pattern: %v", err)
}
return err
} Prevention
- Use the canonical anchored form (^|\.)domain\.tld$ for every rdns pattern.
- Escape dots in domain names (\. not .).
- Avoid PCRE-only syntax; test with regexp.Compile in CI.
- Convert globs (*.example.com) to regexes rather than pasting them directly.
When it happens
Trigger: A bots JSONL entry contains "rdns":[...] where one pattern fails regexp.Compile: unbalanced parentheses, dangling backslash, invalid repetition like "**", or PCRE-only syntax such as lookaheads or backreferences that RE2 rejects.
Common situations: Hand-written domain regexes with typos; copying shell glob patterns (*.googlebot.com) instead of regexes; pasting PCRE patterns from other tools (grep -P, PHP); forgetting to escape dots (cosmetic but the compile error usually indicates worse).
Related errors
- invalid user_agent regex for bot entry '%s' in %s: %w
- invalid path regex '%s' for bot entry '%s' in %s: %w
- empty rdns pattern for bot entry '%s' in %s
- out of bound gid
- ErrFeatureNameInvalid
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/4548e543756b4fa5.
Report an issue: GitHub.