crowdsecurity/crowdsec · error

bot entry '%s' in %s has no identity verification (need at l

Error message

bot entry '%s' in %s has no identity verification (need at least one of ips/ranges/rdns)

What it means

botFileInit enforces that every bot entry carries at least one identity-verification mechanism: ips, ranges, or rdns. This error is thrown when an entry has none of these, i.e. it would only (potentially) match on user_agent/paths. UA-only entries are rejected by design because a User-Agent is trivially spoofable — identity must be corroborated by IP, CIDR, or forward-confirmed reverse DNS.

Source

Thrown at pkg/exprhelpers/botfile.go:63

	return regexp.Compile("(?i)" + pattern) // Force case insensitive match
}

func botFileInit(filename string, line string) error {
	entry := &botEntry{}

	dec := json.NewDecoder(strings.NewReader(line))
	dec.DisallowUnknownFields()

	if err := dec.Decode(entry); err != nil {
		return fmt.Errorf("failed to parse JSON line in %s: %w", filename, err)
	}

	if entry.Name == "" {
		return fmt.Errorf("missing mandatory 'name' field in %s: %s", filename, line)
	}

	if len(entry.IPs)+len(entry.Ranges)+len(entry.RDNS) == 0 {
		return fmt.Errorf("bot entry '%s' in %s has no identity verification (need at least one of ips/ranges/rdns)", entry.Name, filename)
	}

	var err error

	if entry.UserAgent != "" {
		if entry.uaRegex, err = compileBotRegex(entry.UserAgent); err != nil {
			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)
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Add at least one identity source to the entry: verified ips (e.g. Google/BD published ranges), a "ranges" CIDR list, or an "rdns" pattern.
  2. For crawler bots, fetch the operator's official published IP ranges (e.g. https://developers.google.com/search/apis/ipranges) and put them in "ranges".
  3. Use "rdns":["(^|\.)googlebot\.com$"] style forward-confirmed reverse-DNS patterns when IPs are not published.
  4. If you truly want UA-only matching, this loader is not the place — use a parse/expression rule in a scenario instead.

Example fix

// before
{"name":"googlebot","user_agent":"Googlebot"}
// after
{"name":"googlebot","user_agent":"Googlebot","rdns":["(^|\.)googlebot\.com$"]}
Defensive patterns

Strategy: validation

Validate before calling

var probe struct {
	IPs    []string `json:"ips"`
	Ranges []string `json:"ranges"`
	RDNS   []string `json:"rdns"`
}
_ = json.Unmarshal([]byte(line), &probe)
valid := len(probe.IPs)+len(probe.Ranges)+len(probe.RDNS) > 0

Try / catch

if err := exprhelpers.FileInit(botFile, "bots"); err != nil {
	if strings.Contains(err.Error(), "no identity verification") {
		log.Errorf("entry needs ips/ranges/rdns: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: A bots JSONL line defines only name (+ optionally user_agent/paths) with no "ips", "ranges", or "rdns" arrays, e.g. {"name":"googlebot","user_agent":"Googlebot"}.

Common situations: Porting a UA-only blocklist from another tool where UA matching alone was accepted; authoring a new bot entry and assuming UA is sufficient; copying an example that showed only the UA field; empty arrays like "ips":[] also trigger since len sums to 0.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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