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

  1. Set a real Pattern or delete the placeholder rule from the list.
  2. Validate rules at config load: for Type==regex require non-empty Pattern (and Name).
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/7fe64f7d42afb1e1. Report an issue: GitHub.