JuliusBrussee/caveman · error · ErrInvalidRule
%w: rule %q: %s
Error message
%w: rule %q: %s
What it means
regexp.Compile failed for the rule's Pattern; the error wraps ErrInvalidRule and includes the rule's Name plus the underlying regexp/syntax error text (position and reason). The Go RE2 engine rejects several constructs common elsewhere: backreferences ((\1)), lookaround ((?=...)), and some invalid repetition/escape spellings.
Source
Thrown at shared/platform/redact/payload.go:586
// 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,
re: re,
replIntroducesNeedle: introducesNeedle([]byte(repl)),
// The replacement is operator-supplied data, not a regexp
// template: a literal replace keeps "$1" from expanding a capturedView on GitHub (pinned to 27d5a3981a)
Solutions
- Rewrite the pattern for RE2: replace lookbehind with a captured group (token:\s*(\w+)) and backreferences with explicit alternation or a different strategy.
- Test patterns with regexp.Compile in a unit test over the rule fixture so syntax errors surface in CI, not in the capture path.
- If the pattern came from string interpolation, use regexp.QuoteMeta for literal parts.
Example fix
// before
{Name: "after-token", Type: redact.RuleTypeRegex, Pattern: `(?<=token:)[A-Za-z0-9]+`} // lookbehind unsupported
// after
{Name: "after-token", Type: redact.RuleTypeRegex, Pattern: `token:\s*([A-Za-z0-9]+)`} // capture group, replacement keeps $-free literal Defensive patterns
Strategy: validation
Validate before calling
func compileAllRules(rules []redact.Rule) error {
for _, r := range rules {
if r.Type != redact.RuleTypeRegex { continue }
if _, err := regexp.Compile(r.Pattern); err != nil {
return fmt.Errorf("rule %q: %w", r.Name, err)
}
}
return nil
} Type guard
func compiles(p string) bool { _, err := regexp.Compile(p); return err == nil } Try / catch
if _, _, err := redact.Payload(body, rules); err != nil {
if errors.Is(err, redact.ErrInvalidRule) && strings.Contains(err.Error(), ": error parsing") {
// rewrite the pattern for RE2 (no lookaround/backreferences) and redeploy rules
}
} Prevention
- Author patterns for Go's RE2 dialect; test every rule with regexp.Compile in CI.
- Never interpolate unescaped user text into a pattern — use regexp.QuoteMeta for literal parts.
- If coming from PCRE, mechanically replace lookbehind with capture groups and drop backreferences.
When it happens
Trigger: A rule authored with PCRE syntax — e.g. Pattern: `"(?<=token:)\s+\w+"` or `(a+)\1` — passed to redact.Payload; the compile step runs before any body is processed, so the error fires on the first call regardless of body content.
Common situations: Porting redaction rules written for PCRE/Perl/ripgrep-style engines; regex validated in a JS UI (different dialect) and deployed to the Go backend; unescaped user input interpolated into a pattern (a stray '(' breaks it).
Related errors
- %w: %q (rule %q)
- %w: empty name
- %w: rule %q has an empty pattern
- %w: rule %q matches the empty string
- cave_budget_denomination_ambiguous
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/ac4a7fd84bd4a87f.
Report an issue: GitHub.