aaif-goose/goose · error

No agent-visible tool pair found for tool id: {}

Error message

No agent-visible tool pair found for tool id: {}

What it means

Before summarizing a tool call, goose gathers the agent-visible messages for the session and requires BOTH a MessageContent::ToolRequest and a MessageContent::ToolResponse whose id equals the requested tool id. This error means one half of the pair is missing (or the id matches nothing), so the call cannot be summarized.

Source

Thrown at crates/goose/src/context_mgmt/mod.rs:439

            })
        })
        .cloned()
        .collect::<Vec<_>>();
    let matching_messages =
        Conversation::new_unvalidated(matching_messages).agent_visible_messages();

    let has_request = matching_messages.iter().any(|message| {
        message.content.iter().any(
            |content| matches!(content, MessageContent::ToolRequest(request) if request.id == tool_id),
        )
    });
    let has_response = matching_messages.iter().any(|message| {
        message.content.iter().any(
            |content| matches!(content, MessageContent::ToolResponse(response) if response.id == tool_id),
        )
    });
    if !has_request || !has_response {
        return Err(anyhow::anyhow!(
            "No agent-visible tool pair found for tool id: {}",
            tool_id
        ));
    }
    Ok(matching_messages)
}

pub async fn summarize_tool_call(
    provider: &dyn Provider,
    model_config: &ModelConfig,
    session_id: &str,
    conversation: &Conversation,
    tool_id: &str,
) -> Result<Message> {
    let matching_messages = agent_visible_tool_pair(conversation, tool_id)?;

    let formatted = matching_messages
        .iter()

View on GitHub (pinned to 3810898a74)

Solutions

  1. Verify the tool id refers to a completed call in the CURRENT conversation — both a ToolRequest and a ToolResponse with that exact id must be present in agent-visible messages
  2. If the tool is still running, wait for its response before summarizing
  3. Re-discover valid ids by scanning the live conversation instead of using logged/historical ids
Defensive patterns

Strategy: validation

Validate before calling

use goose::messages::{MessageContent, Conversation};

fn has_agent_visible_tool_pair(conversation: &Conversation, tool_id: &str) -> bool {
    let mut has_request = false;
    let mut has_response = false;
    for m in conversation.messages().iter().filter(|m| m.is_agent_visible()) {
        for c in &m.content {
            match c {
                MessageContent::ToolRequest(r) if r.id == tool_id => has_request = true,
                MessageContent::ToolResponse(r) if r.id == tool_id => has_response = true,
                _ => {}
            }
        }
    }
    has_request && has_response
}

// before summarize_tool_call:
if !has_agent_visible_tool_pair(&conversation, tool_id) { /* skip or re-fetch the id */ }

Try / catch

match summarize_tool_call(provider, &model_config, session_id, &conversation, tool_id).await {
    Err(e) if e.to_string().starts_with("No agent-visible tool pair found") => {
        // the id is stale or the call is incomplete: re-scan the live conversation for valid ids
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling summarize_tool_call with a tool id that is stale (from before a compaction/truncation that dropped one half), a request whose response has not arrived yet (tool still running), or an id typo. Messages hidden from the agent also never match.

Common situations: Summarizing old tool calls after context truncation removed the request or response; summarizing while the tool is still executing; replaying a logged tool id against a different conversation.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/f85cb09318df7a72. Report an issue: GitHub.