JuliusBrussee/caveman · error · ErrInvalidRule

redact: invalid redaction rule

Error message

redact: invalid redaction rule

What it means

ErrInvalidRule is returned by compileOrgRules when a redaction Rule row cannot be turned into a working matcher: the rule Name is empty/whitespace, the Pattern is empty, the regex fails to compile, or the pattern matches the empty string (which would rewrite the entire body). Payload surfaces it before any redaction is attempted, so one bad org rule blocks the whole pass.

Solutions

  1. Fix the offending rule row: give it a non-empty name and a compiling, non-empty matching pattern
  2. Test the pattern with regexp.Compile before persisting it to redaction_rules
  3. Validate rules at rule-management time (admin API/UI) so bad rows never reach the redaction pass
  4. Read the wrapped %w detail in the error — it names the rule and the exact structural problem

Example fix

// before
redact.Rule{Name: "", Pattern: "a*"} // invalid: empty name, empty-matching pattern
// after
redact.Rule{Name: "api-key", Pattern: "(?i)sk-[a-z0-9]{16,}"}
Defensive patterns

Strategy: validation

Validate before calling

func validRule(r redact.Rule) error {
    if strings.TrimSpace(r.Name) == "" { return fmt.Errorf("rule name required") }
    if r.Pattern == "" { return fmt.Errorf("rule %q pattern required", r.Name) }
    if _, err := regexp.Compile(r.Pattern); err != nil { return fmt.Errorf("rule %q: %w", r.Name, err) }
    return nil
}

Try / catch

redacted, report, err := redact.Payload(body, rules)
if err != nil && errors.Is(err, redact.ErrInvalidRule) {
    log.Errorf("bad redaction rule, skipping pass: %v", err) // err names the rule
    return err
}

Prevention

When it happens

Trigger: Inserting a redaction_rules row with an empty name or pattern; a rule pattern that is not a valid Go regexp (e.g. "("); a pattern like "a*" that matches the empty string; passing org rules from a DB migration that defaulted columns to ''.

Common situations: An admin saving a rule via a UI that allows blank pattern fields; regexes copied from other engines (PCRE/RE2 syntax differences) failing Go's regexp.Compile; seeding scripts inserting placeholder rows with empty values.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/b2bbbb255f643c5b. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/redact/payload.go:72

	RuleTypeJSONPath = "json_path"
	RuleTypeHeader   = "header"
	RuleTypeBuiltin  = "builtin"
)

// Origin values distinguish the built-in floor from operator-supplied rules in
// the report.
const (
	OriginBuiltin = "builtin"
	OriginOrg     = "org"
)

var (
	// ErrBodyTooLarge means the body exceeded MaxPayloadBytes.
	ErrBodyTooLarge = errors.New("redact: payload exceeds redaction size limit")
	// ErrInvalidRule means a rule could not be compiled or was structurally
	// unusable (empty name/pattern, or a pattern that matches the empty
	// string and would therefore rewrite the entire body).
	ErrInvalidRule = errors.New("redact: invalid redaction rule")
	// ErrRuleUnsupported means the rule's type is not implementable against an
	// opaque body by this pass.
	ErrRuleUnsupported = errors.New("redact: unsupported redaction rule type")
)

// Rule is one enabled row of the redaction_rules table, minus the columns that
// are the caller's business. Field names and JSON tags are pinned to that
// table's columns so the two cannot drift.
//
// Deliberately absent:
//   - id / organization_id / project_id — tenancy is the caller's concern.
//   - enabled — filtering disabled rows is the caller's concern; a rule that
//     reaches Payload is a rule that runs.
//
// A row with rule_type='builtin' is accepted and treated as a no-op reference:
// the built-in floor is unconditional, so such a row can neither add to nor
// subtract from it.
type Rule struct {

View on GitHub (pinned to 3ee70a1026)