{"record":{"id":"4d0bfd96c590fab4","repo":"Hmbown/CodeWhale","slug":"compaction-returned-a-tool-call-instead-of-a-completed","errorCode":null,"errorMessage":"Compaction returned a tool call instead of a completed handoff; original conversation was preserved.","messagePattern":"Compaction returned a tool call instead of a completed handoff; original conversation was preserved\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/compaction.rs","lineNumber":1674,"sourceCode":"            &cost_route,\n            &response.usage,\n        );\n\n        // Usage above is already billed; a provider-declared incomplete\n        // summary must still fail rather than replace the session history\n        // with a fragment.\n        if codewhale_models::is_incomplete_stop_reason(response.stop_reason.as_deref()) {\n            anyhow::bail!(\n                \"Compaction summary response incomplete: provider stop reason `{}`; the partial summary was not accepted.\",\n                codewhale_models::stop_reason_detail(response.stop_reason.as_deref())\n            );\n        }\n        if response\n            .content\n            .iter()\n            .any(|block| matches!(block, ContentBlock::ToolUse { .. }))\n        {\n            anyhow::bail!(\n                \"Compaction returned a tool call instead of a completed handoff; original conversation was preserved.\"\n            );\n        }\n\n        let summary = response\n            .content\n            .iter()\n            .filter_map(|block| match block {\n                ContentBlock::Text { text, .. } => Some(text.clone()),\n                _ => None,\n            })\n            .collect::<Vec<_>>()\n            .join(\"\\n\");\n\n        if let Err(error) = validate_compaction_summary(&summary) {\n            if quality_retry_used {\n                return Err(error.context(\n                    \"Compaction summary remained unusable after one conservative retry; \\","sourceCodeStart":1656,"sourceCodeEnd":1692,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/compaction.rs#L1656-L1692","documentation":"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.","triggerScenarios":"The summary request completes but `response.content` contains at least one `ContentBlock::ToolUse` — the model emitted a tool call during compaction.","commonSituations":"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.","solutions":["Disable tools / tool_choice for the compaction summary request (force text-only output).","Retry compaction — the model may emit text on a second attempt.","Check the summary prompt states it must return only text, not take actions.","Use a model or configuration that does not auto-invoke tools for this request.","No data lost: the original history is kept; re-run compaction after fixing."],"exampleFix":"// before: tools available during compaction\nlet resp = model.request(req).await?;\n// after: force plain text\nlet resp = model.request(req.with_tools_disabled()).await?;","handlingStrategy":"validation","validationCode":"fn has_tool_use(content: &[ContentBlock]) -> bool {\n    content.iter().any(|b| matches!(b, ContentBlock::ToolUse { .. }))\n}","typeGuard":"if has_tool_use(&response.content) { /* reject as non-summary */ }","tryCatchPattern":"match compaction::summarize(&history) {\n    Err(e) if e.to_string().contains(\"tool call instead\") => retry_text_only(),\n    other => other,\n}","preventionTips":["Disable tool access for the compaction request","Force text-only output (tool_choice none / no tools)","Keep the summary prompt scoped to 'return text only'","Preserve the original conversation until validation passes"],"tags":["compaction","tool-call","llm-response"],"backgroundTag":"unexpected-response-shape","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}