JuliusBrussee/caveman · error · ErrInvalidRule
%w: rule %q has an empty pattern
Error message
%w: rule %q has an empty pattern
What it means
A regex rule has Pattern == "". An empty pattern compiles to a regex that matches at every position, which would blanket-replace the entire body with placeholders and destroy the capture — the same outcome the 'matches the empty string' guard exists for, caught one step earlier at the trivial case. Rejected with ErrInvalidRule, naming the rule.
Source
Thrown at shared/platform/redact/payload.go:582
sb.WriteByte('\x1e')
switch r.Type {
case RuleTypeBuiltin:
// A reference to the unconditional floor. Nothing to run, and
// nothing it could switch off.
continue
case RuleTypeRegex:
case RuleTypeJSONPath, RuleTypeHeader:
return nil, "", fmt.Errorf("%w: %q (rule %q)", ErrRuleUnsupported, r.Type, r.Name)
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,View on GitHub (pinned to 27d5a3981a)
Solutions
- Set a real Pattern or delete the placeholder rule from the list.
- Validate rules at config load: for Type==regex require non-empty Pattern (and Name).
- Add a JSON Schema / cue definition for the rules file with required fields so malformed rules fail before runtime.
Example fix
// before
rules := []redact.Rule{{Name: "api-key", Type: redact.RuleTypeRegex}} // Pattern missing
// after
rules := []redact.Rule{{Name: "api-key", Type: redact.RuleTypeRegex, Pattern: `(?i)api[_-]?key"?\s*[:=]\s*"?[A-Za-z0-9]{16,}`}} Defensive patterns
Strategy: validation
Validate before calling
func validatePatterns(rules []redact.Rule) error {
for _, r := range rules {
if r.Type == redact.RuleTypeRegex && r.Pattern == "" {
return fmt.Errorf("rule %q: empty pattern", r.Name)
}
}
return nil
} Type guard
func hasPattern(r redact.Rule) bool { return r.Pattern != "" } Try / catch
if _, _, err := redact.Payload(body, rules); err != nil {
if errors.Is(err, redact.ErrInvalidRule) && strings.Contains(err.Error(), "empty pattern") {
// drop or fill the placeholder rule, then retry
}
} Prevention
- Enforce required fields in the rules file schema (JSON Schema required: [name, type, pattern]).
- Validate the rule set at config load with a startup check.
- Treat zero-valued Rule structs in a list as a config bug, not a no-op.
When it happens
Trigger: A Rule with Type RuleTypeRegex and an empty Pattern reaches compileOrgRules — typically a rule object created from config where the pattern key was absent, commented out, or the field name mistyped.
Common situations: Optional config sections that decode to zero-valued rules; a rule template/comment copied and not filled in; JSON schema not enforcing minLength on pattern
Related errors
- %w: empty name
- cave_budget_denomination_ambiguous
- cave_budget_max_invalid
- cave_budget_output_floor_invalid
- cave_budget_on_exhausted_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/7fe64f7d42afb1e1.
Report an issue: GitHub.