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
- Fix the offending rule row: give it a non-empty name and a compiling, non-empty matching pattern
- Test the pattern with regexp.Compile before persisting it to redaction_rules
- Validate rules at rule-management time (admin API/UI) so bad rows never reach the redaction pass
- 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
- Validate name/pattern/regex at rule-creation time (admin UI and API), not just at redaction time
- Avoid empty-matching patterns like a* or .*? that rewrite the whole body
- Use Go's regexp (RE2) syntax when authoring patterns for other tools
- Add a CHECK constraint or validator on redaction_rules before insert
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
- apiKey, baseURL, and agent are required
- ASGI context resolver or request bounds are invalid
- bedrock endpoint must be runtime or mantle
- cave_mastra_max_steps_invalid
- cave_memory_agent_invalid
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)