From 3a65236210d7231923059ff1f954fd6a2d67591d Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Mon, 24 Aug 2026 13:59:33 +0000 Subject: [PATCH] fix: honest compaction failure message and fast-fail when no tool responses exist (#10500) Signed-off-by: Michael Neale Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Michael Neale --- .../goose-context-management/src/summarize.rs | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/crates/goose-context-management/src/summarize.rs b/crates/goose-context-management/src/summarize.rs index 4cb060d5b..9c7913687 100644 --- a/crates/goose-context-management/src/summarize.rs +++ b/crates/goose-context-management/src/summarize.rs @@ -21,6 +21,7 @@ struct SummarizeContext { messages: String, } +#[derive(Debug)] pub struct Summary { pub message: Message, pub usage: ProviderUsage, @@ -127,6 +128,7 @@ pub async fn summarize( messages: &[Message], ) -> Result { let request = vec![Message::user().with_text(SUMMARIZE_REQUEST_TEXT)]; + let has_tool_responses = messages.iter().any(has_tool_response); for (attempt, &remove_percent) in REMOVAL_PERCENTAGES.iter().enumerate() { let filtered = filter_tool_responses(messages, remove_percent); @@ -158,6 +160,11 @@ pub async fn summarize( usage, }); } + Err(ProviderError::ContextLengthExceeded(_)) if !has_tool_responses => { + return Err(anyhow::anyhow!( + "Failed to compact: the base prompt (system prompt, tool schemas, and conversation) exceeds the model's effective context window, and there are no tool responses to remove. Use a model or configuration with a larger usable context, disable some extensions to reduce the tool-schema payload, or start a new session." + )); + } Err(ProviderError::ContextLengthExceeded(_)) if attempt < REMOVAL_PERCENTAGES.len() - 1 => {} Err(ProviderError::ContextLengthExceeded(_)) => { @@ -173,3 +180,85 @@ pub async fn summarize( "Unexpected: exhausted all attempts without returning" )) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::model::CompactionModel; + use crate::templates::Templates; + use async_trait::async_trait; + use rmcp::model::CallToolResult; + use std::sync::atomic::{AtomicUsize, Ordering}; + + struct OverflowingModel { + request_count: AtomicUsize, + } + + impl OverflowingModel { + fn new() -> Self { + Self { + request_count: AtomicUsize::new(0), + } + } + + fn request_count(&self) -> usize { + self.request_count.load(Ordering::Relaxed) + } + } + + #[async_trait] + impl CompactionModel for OverflowingModel { + async fn complete( + &self, + _system: &str, + _messages: &[Message], + ) -> Result<(Message, ProviderUsage), ProviderError> { + self.request_count.fetch_add(1, Ordering::Relaxed); + Err(ProviderError::ContextLengthExceeded( + "Prompt exceeds context limit".to_string(), + )) + } + } + + #[tokio::test] + async fn summarize_without_tool_responses_fails_fast() { + let model = OverflowingModel::new(); + let messages = vec![Message::user().with_text("oversized conversation")]; + + let error = summarize(&model, None, &Templates::default(), &messages) + .await + .unwrap_err(); + let error_message = error.to_string(); + + assert_eq!(model.request_count(), 1); + assert!(error_message.contains("there are no tool responses to remove")); + assert!(!error_message.contains("even after removing all tool responses")); + assert!(error_message.contains("larger usable context")); + assert!(error_message.contains("disable some extensions")); + assert!(error_message.contains("start a new session")); + } + + #[tokio::test] + async fn summarize_with_tool_responses_preserves_exhausted_removal_error() { + let model = OverflowingModel::new(); + let messages = vec![ + Message::user().with_text("please read the file"), + Message::user().with_tool_response( + "tool_0", + Ok(CallToolResult::success(vec![ + rmcp::model::ContentBlock::text("contents"), + ])), + ), + ]; + + let error = summarize(&model, None, &Templates::default(), &messages) + .await + .unwrap_err(); + + assert_eq!(model.request_count(), REMOVAL_PERCENTAGES.len()); + assert_eq!( + error.to_string(), + "Failed to compact: context limit exceeded even after removing all tool responses" + ); + } +}