Hmbown/CodeWhale · error · anyhow::Error

hunt_verdict task update must be a string

Error message

hunt_verdict task update must be a string

What it means

While applying task updates, if the updates object contains a 'hunt_verdict' key, its value must be a JSON string (value.as_str()). Any other JSON type — boolean, number, null, array, object — is rejected before normalize_hunt_verdict runs. The verdict string itself is then normalized; invalid strings fail in normalize_hunt_verdict with a separate error.

Source

Thrown at crates/tui/src/task_manager.rs:2126

                .context("Failed to parse gate task update")?;
            let summary = format!("Gate {} {}: {}", gate.gate, gate.status, gate.summary);
            task.gates.retain(|existing| existing.id != gate.id);
            task.gates.push(gate.clone());
            push_timeline_entry(
                task,
                TaskTimelineEntry {
                    timestamp: now,
                    kind: "gate".to_string(),
                    summary: summarize_text(&summary, TIMELINE_SUMMARY_LIMIT),
                    detail_path: gate.log_path,
                },
            );
        }

        if let Some(value) = updates.get("hunt_verdict") {
            let raw = value
                .as_str()
                .ok_or_else(|| anyhow!("hunt_verdict task update must be a string"))?;
            let verdict = normalize_hunt_verdict(raw)?;
            if task.hunt_verdict.as_deref() != Some(verdict) {
                task.hunt_verdict = Some(verdict.to_string());
                push_timeline_entry(
                    task,
                    TaskTimelineEntry {
                        timestamp: now,
                        kind: "hunt_verdict".to_string(),
                        summary: format!("Hunt verdict updated: {verdict}"),
                        detail_path: None,
                    },
                );
            }
        }

        if let Some(value) = updates.get("attempt") {
            let attempt: TaskAttemptRecord = serde_json::from_value(value.clone())
                .context("Failed to parse attempt task update")?;

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Pass the verdict as a JSON string: {"hunt_verdict": "clean"} (use whatever verdict spelling normalize_hunt_verdict accepts).
  2. Omit the key entirely if no verdict update is needed.
  3. Validate the updates payload shape before calling the update API.

Example fix

// before
updates = { "hunt_verdict": true }
updates = { "hunt_verdict": { "result": "clean" } }

// after
updates = { "hunt_verdict": "clean" }
Defensive patterns

Strategy: type-guard

Type guard

function isValidHuntVerdictUpdate(updates: Record<string, unknown>): boolean {
  const v = updates['hunt_verdict'];
  return v === undefined || typeof v === 'string';
}

Try / catch

if (updates.hunt_verdict !== undefined && typeof updates.hunt_verdict !== 'string') {
  updates.hunt_verdict = String(updates.hunt_verdict);
}
try { await applyTaskUpdates(id, updates); }
catch (e) {
  if (e.message.includes('hunt_verdict task update must be a string')) { /* fix payload to string, retry once */ }
  else throw e;
}

Prevention

When it happens

Trigger: Applying task updates like {"hunt_verdict": true}, {"hunt_verdict": 1}, or {"hunt_verdict": {"verdict": "clean"}} — the value is present but not a string.

Common situations: A model or integration emits a boolean verdict (true/false) or a nested object instead of the expected string literal such as 'clean' or 'suspicious'.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/a337556a4df986cb. Report an issue: GitHub.