Hmbown/CodeWhale · error

single target returned a result

Error message

single target returned a result

What it means

In `execute` (crates/tui/src/tools/subagent/coord.rs:506), when a single followup target was requested (`single.is_some()`), any error aborts immediately, so the code assumes `results` must contain exactly one payload and calls `results.pop().expect("single target returned a result")`. A panic means the single-target loop pushed nothing to `results` before reaching this line — e.g. the loop body never ran or the success path was skipped.

Solutions

  1. Verify how `single` is derived and ensure it is populated only when the iteration will actually produce one result.
  2. Guard with `results.pop().ok_or_else(|| ToolError::execution_failed("single target produced no result"))?` instead of `expect`.
  3. Add a test for followup with a single target that yields no loop iteration.
  4. Check whether the loop's `continue` paths can skip the single target entirely.

Example fix

// before
results.pop().expect("single target returned a result")
// after
results.pop().ok_or_else(|| ToolError::execution_failed("single target returned no result"))?
Defensive patterns

Strategy: validation

Validate before calling

// Validate target list before dispatch
if targets.is_empty() {
    return Err(ToolError::execution_failed("followup requires at least one target"));
}

Type guard

fn single_result(results: Vec<serde_json::Value>) -> Option<serde_json::Value> {
    if results.len() == 1 { results.into_iter().next() } else { None }
}

Try / catch

// Fallible pop instead of expect
let payload = results.pop()
    .ok_or_else(|| ToolError::execution_failed("single target returned no result"))?;

Prevention

When it happens

Trigger: Executing a `followup` action with exactly one target where `targets` iterated zero times (empty/missing single target entry) while `single` was still `Some`, causing the `pop()` on an empty `results` vec to panic.

Common situations: A followup call whose target list deserialized to `Some` but contained an empty or invalid entry that the loop skipped; a refactor that changed how `single` is populated relative to the iteration source.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/b416eb7c6cdfffef. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/subagent/coord.rs:506

                ));
            }
            ids
        };
        let mut seen = std::collections::HashSet::new();
        targets.retain(|target| seen.insert(target.clone()));
        let mut results = Vec::new();
        let mut errors = Vec::new();
        // Each mutation and both hierarchy checks share the manager write lock.
        // A target failure cannot erase successful results from another target.
        for target in targets {
            match self.followup_one(&target, message, context).await {
                Ok(payload) => results.push(payload),
                Err(error) if single.is_some() => return Err(error),
                Err(error) => errors.push(json!({"from": target, "error": error.to_string()})),
            }
        }
        let payload = if single.is_some() {
            results.pop().expect("single target returned a result")
        } else {
            json!({"action": "followup", "results": results, "errors": errors})
        };
        let mut result = ToolResult::json(&payload)
            .map_err(|error| ToolError::execution_failed(error.to_string()))?;
        result.metadata = Some(payload.clone());
        Ok(result)
    }
}

// ── agents/interrupt ─────────────────────────────────────────────────────

pub struct AgentsInterruptTool {
    manager: SharedSubAgentManager,
    /// Optional caller identity for fail-closed self-interrupt checks.
    caller_agent_id: Option<String>,
}

View on GitHub (pinned to 433685b202)