crowdsecurity/crowdsec · error

invalid IP ' ' for bot entry ' ' in

Error message

invalid IP '%s' for bot entry '%s' in %s: %w

What it means

botFileInit parses each element of the entry's "ips" array with netip.ParseAddr and stores it in an in-memory set. This error is thrown when a listed IP is not a valid textual IP address (IPv4 or IPv6). The wrapped error is the netip.ParseAddr parse failure, and the message identifies the bad value, entry, and file.

Solutions

  1. Fix the malformed value to a bare IPv4/IPv6 address, e.g. "192.0.2.10" or "2001:db8::1".
  2. If the value is a network like "1.2.3.0/24", move it to the "ranges" array — ParseAddr rejects CIDR notation.
  3. If it is a hostname, resolve it to an address or drop it; this loader does no DNS resolution for ips.
  4. Strip whitespace/BOM/quotes from each element; verify with `python3 -c "import ipaddress; ipaddress.ip_address('VALUE')"` or similar.
  5. Remove empty strings from the ips array.

Example fix

// before
{"name":"badbot","ips":["203.0.113.0/24","203.0.113.256"]}
// after
{"name":"badbot","ips":[],"ranges":["203.0.113.0/24"]}
Defensive patterns

Strategy: validation

Validate before calling

for _, ip := range entry.IPs {
	if _, err := netip.ParseAddr(strings.TrimSpace(ip)); err != nil {
		// reject before FileInit
	}
}
valid := err == nil

Try / catch

if err := exprhelpers.FileInit(botFile, "bots"); err != nil {
	var perr *net.ParseError
	if strings.Contains(err.Error(), "invalid IP") {
		log.Errorf("bad ips element: %v", err)
	}
	_ = perr
	return err
}

Prevention

When it happens

Trigger: A bots JSONL entry contains "ips":[...] with a malformed element: a CIDR like "1.2.3.0/24" (belongs in ranges), a hostname, "1.2.3.256", an empty string, or an address with a zone like "fe80::1%eth0".

Common situations: Putting CIDR ranges into "ips" instead of "ranges"; copy-pasting hostnames from a blocklist meant for DNS resolution; trailing whitespace or invisible characters; octet overflow typos; export scripts writing placeholder values like "<ip>".

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at pkg/exprhelpers/botfile.go:88

			return fmt.Errorf("invalid user_agent regex for bot entry '%s' in %s: %w", entry.Name, filename, err)
		}
	}

	for _, p := range entry.Paths {
		re, err := compileBotRegex(p)
		if err != nil {
			return fmt.Errorf("invalid path regex '%s' for bot entry '%s' in %s: %w", p, entry.Name, filename, err)
		}

		entry.pathRegexes = append(entry.pathRegexes, re)
	}

	entry.ipSet = make(map[netip.Addr]struct{}, len(entry.IPs))

	for _, ip := range entry.IPs {
		addr, err := netip.ParseAddr(ip)
		if err != nil {
			return fmt.Errorf("invalid IP '%s' for bot entry '%s' in %s: %w", ip, entry.Name, filename, err)
		}

		entry.ipSet[addr.Unmap()] = struct{}{}
	}

	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 == "" {

View on GitHub (pinned to 909b515798)