Hmbown/CodeWhale · error · anyhow::Error
Invalid auto_review.{kind}[{index}].text_contains: user-inte
Error message
Invalid auto_review.{kind}[{index}].text_contains: user-intent matching was retired; scope the rule with tool and/or action_kind. What it means
Config validation rejects auto_review rules that still set text_contains (crates/tui/src/config.rs:3030): user-intent matching was retired, so a rule must be scoped by tool and/or action_kind instead. This fails config load/startup, not at review time.
Source
Thrown at crates/tui/src/config.rs:3030
fn has_matcher(&self) -> bool {
self.tool
.as_deref()
.is_some_and(|value| !value.trim().is_empty())
|| self
.action_kind
.as_deref()
.is_some_and(|value| !value.trim().is_empty())
}
}
fn validate_auto_review_rules(kind: &str, rules: &[AutoReviewRuleConfig]) -> Result<()> {
for (index, rule) in rules.iter().enumerate() {
if rule
.text_contains
.as_deref()
.is_some_and(|value| !value.trim().is_empty())
{
anyhow::bail!(
"Invalid auto_review.{kind}[{index}].text_contains: user-intent matching was retired; scope the rule with tool and/or action_kind."
);
}
if !rule.has_matcher() {
anyhow::bail!(
"Invalid auto_review.{kind}[{index}]: set at least one of tool or action_kind."
);
}
if let Some(action_kind) = rule.action_kind.as_deref() {
let normalized = action_kind.trim().to_ascii_lowercase().replace('-', "_");
if parse_auto_review_action_kind(&normalized).is_none() {
anyhow::bail!(
"Invalid auto_review.{kind}[{index}].action_kind '{action_kind}': expected read, write, shell, external, publish, or destructive."
);
}
if kind == "allow"
&& !matches!(
normalized.as_str(),View on GitHub (pinned to 8880682c63)
Solutions
- Delete text_contains from the rule
- Scope the same intent with tool = "<tool-id>" and/or action_kind = "read|write|shell|external|publish|destructive"
- Re-load the config to confirm validation passes
Example fix
# config.toml - before [[auto_review.deny]] text_contains = "rm -rf" # config.toml - after [[auto_review.deny]] tool = "bash" action_kind = "destructive"
Defensive patterns
Strategy: validation
Validate before calling
// Config linter pass over rules before load
for r in &cfg.auto_review.deny {
assert!(r.text_contains.is_none(), "text_contains is retired; use tool/action_kind");
} Type guard
fn uses_retired_matcher(rule: &AutoReviewRuleConfig) -> bool {
rule.text_contains.as_deref().is_some_and(|v| !v.trim().is_empty())
} Try / catch
// Fail config load with the rule's index so the user can jump to it
if uses_retired_matcher(&rule) {
return Err(anyhow!("auto_review[{i}] uses retired text_contains"));
} Prevention
- After upgrades, run the config validator before launching
- Scope review rules to tools or action kinds, never to prompt text
When it happens
Trigger: Carrying a pre-retirement config forward after an upgrade; hand-writing a rule keyed on prompt text; docs or snippets from an older version.
Common situations: Version upgrades where the old matcher was removed; copied config from another machine or blog post.
Related errors
- Invalid auto_review.allow[{index}].action_kind '{action_kind
- ${names.join(" or ")} is required
- Invalid auto_review.{kind}[{index}]: set at least one of too
- Invalid auto_review.{kind}[{index}].action_kind '{action_kin
- ${name} is required
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/24d8d5e6d412c29c.
Report an issue: GitHub.