Hmbown/CodeWhale · error

Compaction returned a tool call instead of a completed…

Error message

Compaction returned a tool call instead of a completed handoff; original conversation was preserved.

What it means

A compaction summary response must be plain text, not a tool invocation. If any content block is a ToolUse, the model tried to call a tool instead of finishing the summary handoff, so compaction aborts and the original conversation is preserved.

Solutions

  1. Disable tools / tool_choice for the compaction summary request (force text-only output).
  2. Retry compaction — the model may emit text on a second attempt.
  3. Check the summary prompt states it must return only text, not take actions.
  4. Use a model or configuration that does not auto-invoke tools for this request.
  5. No data lost: the original history is kept; re-run compaction after fixing.

Example fix

// before: tools available during compaction
let resp = model.request(req).await?;
// after: force plain text
let resp = model.request(req.with_tools_disabled()).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_tool_use(content: &[ContentBlock]) -> bool {
    content.iter().any(|b| matches!(b, ContentBlock::ToolUse { .. }))
}

Type guard

if has_tool_use(&response.content) { /* reject as non-summary */ }

Try / catch

match compaction::summarize(&history) {
    Err(e) if e.to_string().contains("tool call instead") => retry_text_only(),
    other => other,
}

Prevention

When it happens

Trigger: The summary request completes but `response.content` contains at least one `ContentBlock::ToolUse` — the model emitted a tool call during compaction.

Common situations: A model/agent with tools enabled treats the summary prompt as another agent turn and starts calling tools; a misconfigured summary request exposes the tool catalog; aggressive agentic models (auto tool use) act instead of answering.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/4d0bfd96c590fab4. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/compaction.rs:1674

            &cost_route,
            &response.usage,
        );

        // Usage above is already billed; a provider-declared incomplete
        // summary must still fail rather than replace the session history
        // with a fragment.
        if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {
            anyhow::bail!(
                "Compaction summary response incomplete: provider stop reason `{}`; the partial summary was not accepted.",
                codewhale_models::stop_reason_detail(response.stop_reason.as_deref())
            );
        }
        if response
            .content
            .iter()
            .any(|block| matches!(block, ContentBlock::ToolUse { .. }))
        {
            anyhow::bail!(
                "Compaction returned a tool call instead of a completed handoff; original conversation was preserved."
            );
        }

        let summary = response
            .content
            .iter()
            .filter_map(|block| match block {
                ContentBlock::Text { text, .. } => Some(text.clone()),
                _ => None,
            })
            .collect::<Vec<_>>()
            .join("\n");

        if let Err(error) = validate_compaction_summary(&summary) {
            if quality_retry_used {
                return Err(error.context(
                    "Compaction summary remained unusable after one conservative retry; \

View on GitHub (pinned to 73e0f67d83)