JuliusBrussee/caveman · error · ErrRuleUnsupported

%w: %q (rule %q)

Error message

%w: %q (rule %q)

What it means

During rule compilation, a rule whose Type is RuleTypeJSONPath or RuleTypeHeader is rejected with ErrRuleUnsupported. Structured selection rules are not implementable against the byte-oriented redaction pass — Payload runs compiled regexes over the raw body and has no JSON tree or header context to apply those rule types to. The error names the offending type and rule.

Source

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

	var sb strings.Builder
	for _, r := range sorted {
		sb.WriteString(r.Name)
		sb.WriteByte('\x1f')
		sb.WriteString(r.Type)
		sb.WriteByte('\x1f')
		sb.WriteString(r.Pattern)
		sb.WriteByte('\x1f')
		sb.WriteString(r.Replacement)
		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.

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Convert the rule to RuleTypeRegex with an equivalent pattern (e.g. JSONPath '$.user.email' -> a regex anchored on the serialized key \"email\"\s*:).
  2. Filter the rule set to RuleTypeRegex/RuleTypeBuiltin before calling Payload if the same list must serve multiple surfaces.
  3. Surface rule-type support at rule-authoring time (the validator surface the code notes is missing) so unsupported types are rejected at write time, not capture time.

Example fix

// before
rules := []redact.Rule{{Name: "user-email", Type: redact.RuleTypeJSONPath, Pattern: "$.user.email"}}
out, rep, err := redact.Payload(body, rules)

// after
rules := []redact.Rule{{Name: "user-email", Type: redact.RuleTypeRegex, Pattern: `"email"\s*:\s*"[^"]+"`}}
out, rep, err := redact.Payload(body, rules)
Defensive patterns

Strategy: type-guard

Validate before calling

var supportedTypes = map[redact.RuleType]bool{
    redact.RuleTypeBuiltin: true,
    redact.RuleTypeRegex:   true,
}
func onlySupported(rules []redact.Rule) []redact.Rule {
    out := rules[:0]
    for _, r := range rules {
        if supportedTypes[r.Type] { out = append(out, r) }
    }
    return out
}

Type guard

func isSupportedRuleType(t redact.RuleType) bool {
    return t == redact.RuleTypeBuiltin || t == redact.RuleTypeRegex
}

Try / catch

if _, _, err := redact.Payload(body, rules); err != nil {
    if errors.Is(err, redact.ErrRuleUnsupported) {
        // surface to rule author with the rule name/type from the message
    }
}

Prevention

When it happens

Trigger: compileOrgRules (via Payload) receives a rule list containing {Type: RuleTypeJSONPath} or {Type: RuleTypeHeader}, e.g. rules authored for a different redaction surface or copied from a config that assumed structured support.

Common situations: A shared redaction-rules config reused across services where only one supports JSONPath/header rules; a UI rule editor offering types this backend rejects; rules written against the roadmap rather than the shipped implementation.

Related errors


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