Hmbown/CodeWhale · error · anyhow::Error
unsupported hunt_verdict task update '{other}'. Expected one
Error message
unsupported hunt_verdict task update '{other}'. Expected one of: hunting, hunted, wounded, escaped What it means
normalize_hunt_verdict accepts exactly four lowercase verdict strings for hunt-style task updates: hunting, hunted, wounded, escaped. The input is trimmed before matching, but any other value (typos, uppercase forms like 'Hunted', or future verdicts) is rejected with this bail before any task mutation is applied.
Source
Thrown at crates/tui/src/task_manager.rs:2230
&QueueFile {
queue: queue.iter().cloned().collect(),
},
)
}
fn persist_task_locked(&self, task: &TaskRecord) -> Result<()> {
let path = self.tasks_dir.join(format!("{}.json", task.id));
write_json_atomic(&path, task)
}
}
fn normalize_hunt_verdict(raw: &str) -> Result<&'static str> {
match raw.trim() {
"hunting" => Ok("hunting"),
"hunted" => Ok("hunted"),
"wounded" => Ok("wounded"),
"escaped" => Ok("escaped"),
other => bail!(
"unsupported hunt_verdict task update '{other}'. Expected one of: hunting, hunted, wounded, escaped"
),
}
}
/// Outcome of loading the persisted task store at boot: the reconciled task
/// map + queue, plus the ids whose status was flipped running->failed by
/// crash recovery (the only records boot needs to re-persist).
struct LoadedTaskState {
tasks: HashMap<String, TaskRecord>,
queue: VecDeque<String>,
recovered: Vec<String>,
}
fn load_state(tasks_dir: &Path, queue_path: &Path) -> Result<LoadedTaskState> {
let mut tasks = HashMap::new();
let mut recovered = Vec::new();
if tasks_dir.exists() {View on GitHub (pinned to 0c42157ee5)
Solutions
- Map your statuses to the literal set: hunting, hunted, wounded, escaped (lowercase, surrounding whitespace is tolerated).
- Normalize input to trim + lowercase before validation so 'Hunted ' passes.
- If you believe a new verdict is genuinely needed, add it to normalize_hunt_verdict in the product code and its tests rather than string-matching client-side.
Example fix
// before
update_task(id, json!({"hunt_verdict": verdict.as_str()})).await?; // 'HUNTED' fails
// after
let verdict = verdict.trim().to_ascii_lowercase();
if !matches!(verdict.as_str(), "hunting" | "hunted" | "wounded" | "escaped") {
return Err(anyhow!("unknown hunt verdict: {verdict}"));
}
update_task(id, json!({"hunt_verdict": verdict})).await?; Defensive patterns
Strategy: validation
Validate before calling
fn normalize_verdict_or_err(raw: &str) -> Result<&'static str> {
match raw.trim() {
"hunting" | "hunted" | "wounded" | "escaped" => Ok(raw.trim()),
other => Err(anyhow!("unknown hunt verdict: {other}")),
}
} Type guard
fn is_hunt_verdict(s: &str) -> bool {
matches!(s.trim(), "hunting" | "hunted" | "wounded" | "escaped")
} Prevention
- Lowercase and trim verdicts from external systems before sending task updates.
- Keep a single constant list of verdicts shared by producer and consumer.
- Add new verdicts to normalize_hunt_verdict and its tests together; do not special-case client-side.
When it happens
Trigger: Sending a task update with hunt_verdict set to a value outside the four-element set, e.g. 'hunts', 'HUNTED', 'in-progress', or 'killed'; forwarding verdicts from an external tool whose vocabulary differs.
Common situations: Integrating a custom status enum that maps 1:1 except for one or two names; schema drift after a rename; hand-crafted JSON payloads in scripts or tests using informal statuses.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- unknown runtime backend `{other}` (use tmux|inline|vm|ci)
- Failed to update setting: invalid mention_menu_behavior '{va
- Task prompt cannot be empty
- Invalid preallocated task id: expected task_<16hex>
- context_window must be greater than 0
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/e6325035f6876a11.
Report an issue: GitHub.