JuliusBrussee/caveman · error · ErrInvalidRule
%w: rule %q matches the empty string
Error message
%w: rule %q matches the empty string
What it means
The compiled pattern matches the empty string (re.MatchString("") is true — e.g. `.*`, `a*`, `\s*`, `(a|)`). Applied over the whole body with literal replacement, such a pattern matches at every offset and would collapse the entire capture into a wall of placeholders — a self-inflicted total data loss. Compilation refuses it with ErrInvalidRule, naming the rule. Only this and other 'unusable' shapes are rejected; merely greedy-but-bounded patterns are allowed.
Source
Thrown at shared/platform/redact/payload.go:592
default:
return nil, "", fmt.Errorf("%w: %q (rule %q)", ErrRuleUnsupported, r.Type, r.Name)
}
if strings.TrimSpace(r.Name) == "" {
return nil, "", fmt.Errorf("%w: empty name", ErrInvalidRule)
}
if r.Pattern == "" {
return nil, "", fmt.Errorf("%w: rule %q has an empty pattern", ErrInvalidRule, r.Name)
}
re, err := regexp.Compile(r.Pattern)
if err != nil {
return nil, "", fmt.Errorf("%w: rule %q: %s", ErrInvalidRule, r.Name, err)
}
if re.MatchString("") {
// Such a pattern matches at every position and would replace the
// whole body with placeholders. Refuse it rather than destroy the
// capture.
return nil, "", fmt.Errorf("%w: rule %q matches the empty string", ErrInvalidRule, r.Name)
}
repl := r.Replacement
if repl == "" {
repl = "[REDACTED:" + r.Name + "]"
}
out = append(out, compiledRule{
name: r.Name,
origin: OriginOrg,
re: re,
replIntroducesNeedle: introducesNeedle([]byte(repl)),
// The replacement is operator-supplied data, not a regexp
// template: a literal replace keeps "$1" from expanding a captured
// group back into the output.
repl: []byte(repl),
})
}
return out, sb.String(), nil
}View on GitHub (pinned to 27d5a3981a)
Solutions
- Change the quantifier so at least one character is required: `.+` instead of `.*`, `\d+` instead of `\d*`.
- Test every rule with re.MatchString("") in a rules unit test — this is exactly the package's own guard, replicated cheaply at authoring time.
- Add a lint rule in the rule-editor UI flagging unanchored empty-matching patterns.
Example fix
// before
{Name: "free-text", Type: redact.RuleTypeRegex, Pattern: `.*`} // matches empty -> rejected
// after
{Name: "free-text", Type: redact.RuleTypeRegex, Pattern: `.+`} // requires at least one character Defensive patterns
Strategy: validation
Validate before calling
func noEmptyMatches(rules []redact.Rule) error {
for _, r := range rules {
if r.Type != redact.RuleTypeRegex { continue }
re, err := regexp.Compile(r.Pattern)
if err != nil { return fmt.Errorf("rule %q: %w", r.Name, err) }
if re.MatchString("") {
return fmt.Errorf("rule %q: pattern matches empty string", r.Name)
}
}
return nil
} Type guard
func matchesNonEmptyOnly(p string) bool {
re, err := regexp.Compile(p)
return err == nil && !re.MatchString("")
} Try / catch
if _, _, err := redact.Payload(body, rules); err != nil {
if errors.Is(err, redact.ErrInvalidRule) && strings.Contains(err.Error(), "matches the empty string") {
// change '*' quantifiers to '+' for that rule and revalidate the whole set
}
} Prevention
- Prefer '+' over '*' for token-like patterns; require at least one character.
- Run re.MatchString("") as a rule-lint in the editor and in CI.
- Test rules against representative samples so greedy behavior is visible before deploy.
When it happens
Trigger: Any rule whose regex can match zero characters: `.*`, `[A-Z]*`, `\w*`, optional groups that allow empty. The check runs at compile time, so Payload fails on first use with any body.
Common situations: Using '*' where '+' was intended (e.g. `\d*` for digit runs); patterns designed for search contexts where empty matches are harmless; testing the rule with a non-empty sample where the empty match is invisible.
Related errors
- native runtime: invalid decision id
- %w: empty name
- %w: rule %q has an empty pattern
- %w: rule %q: %s
- option not found
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/dffe919e12344fee.
Report an issue: GitHub.