{"record":{"id":"7d4a0040103d9a0f","repo":"JuliusBrussee/caveman","slug":"w-q-rule-q","errorCode":null,"errorMessage":"%w: %q (rule %q)","messagePattern":"%w: %q \\(rule %q\\)","errorType":"validation","errorClass":"ErrRuleUnsupported","httpStatus":null,"severity":"error","filePath":"shared/platform/redact/payload.go","lineNumber":573,"sourceCode":"\tvar sb strings.Builder\n\tfor _, r := range sorted {\n\t\tsb.WriteString(r.Name)\n\t\tsb.WriteByte('\\x1f')\n\t\tsb.WriteString(r.Type)\n\t\tsb.WriteByte('\\x1f')\n\t\tsb.WriteString(r.Pattern)\n\t\tsb.WriteByte('\\x1f')\n\t\tsb.WriteString(r.Replacement)\n\t\tsb.WriteByte('\\x1e')\n\n\t\tswitch r.Type {\n\t\tcase RuleTypeBuiltin:\n\t\t\t// A reference to the unconditional floor. Nothing to run, and\n\t\t\t// nothing it could switch off.\n\t\t\tcontinue\n\t\tcase RuleTypeRegex:\n\t\tcase RuleTypeJSONPath, RuleTypeHeader:\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: %q (rule %q)\", ErrRuleUnsupported, r.Type, r.Name)\n\t\tdefault:\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: %q (rule %q)\", ErrRuleUnsupported, r.Type, r.Name)\n\t\t}\n\n\t\tif strings.TrimSpace(r.Name) == \"\" {\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: empty name\", ErrInvalidRule)\n\t\t}\n\t\tif r.Pattern == \"\" {\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: rule %q has an empty pattern\", ErrInvalidRule, r.Name)\n\t\t}\n\t\tre, err := regexp.Compile(r.Pattern)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: rule %q: %s\", ErrInvalidRule, r.Name, err)\n\t\t}\n\t\tif re.MatchString(\"\") {\n\t\t\t// Such a pattern matches at every position and would replace the\n\t\t\t// whole body with placeholders. Refuse it rather than destroy the\n\t\t\t// capture.","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/redact/payload.go#L555-L591","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Convert the rule to RuleTypeRegex with an equivalent pattern (e.g. JSONPath '$.user.email' -> a regex anchored on the serialized key \\\"email\\\"\\s*:).","Filter the rule set to RuleTypeRegex/RuleTypeBuiltin before calling Payload if the same list must serve multiple surfaces.","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."],"exampleFix":"// before\nrules := []redact.Rule{{Name: \"user-email\", Type: redact.RuleTypeJSONPath, Pattern: \"$.user.email\"}}\nout, rep, err := redact.Payload(body, rules)\n\n// after\nrules := []redact.Rule{{Name: \"user-email\", Type: redact.RuleTypeRegex, Pattern: `\"email\"\\s*:\\s*\"[^\"]+\"`}}\nout, rep, err := redact.Payload(body, rules)","handlingStrategy":"type-guard","validationCode":"var supportedTypes = map[redact.RuleType]bool{\n    redact.RuleTypeBuiltin: true,\n    redact.RuleTypeRegex:   true,\n}\nfunc onlySupported(rules []redact.Rule) []redact.Rule {\n    out := rules[:0]\n    for _, r := range rules {\n        if supportedTypes[r.Type] { out = append(out, r) }\n    }\n    return out\n}","typeGuard":"func isSupportedRuleType(t redact.RuleType) bool {\n    return t == redact.RuleTypeBuiltin || t == redact.RuleTypeRegex\n}","tryCatchPattern":"if _, _, err := redact.Payload(body, rules); err != nil {\n    if errors.Is(err, redact.ErrRuleUnsupported) {\n        // surface to rule author with the rule name/type from the message\n    }\n}","preventionTips":["Keep one rules file per surface; don't share a JSONPath/header rule set with the regex-only redactor.","Translate JSONPath rules to anchored regex equivalents at build time.","Fail at rule-write time (validator/UI) rather than capture time — unsupported types are authoring errors."],"tags":["redaction","config","unsupported-type"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}