aaif-goose/goose · error

Missing tool_use id

Error message

Missing tool_use id

What it means

response_to_message converts a Snowflake Cortex (Anthropic-compatible) non-streaming response into a Message. For each entry in content_list with type "tool_use" it requires a string field tool_use_id to key the tool_request block. If tool_use_id is missing or not a string, conversion fails with 'Missing tool_use id'.

Source

Thrown at crates/goose-provider-types/src/formats/snowflake.rs:249

            }
        }
    };

    // Process all content items in the list
    for content in content_list {
        match content.get("type").and_then(|t| t.as_str()) {
            Some("text") => {
                if let Some(text) = content.get("text").and_then(|t| t.as_str()) {
                    if !text.is_empty() {
                        message = message.with_text(text.to_string());
                    }
                }
            }
            Some("tool_use") => {
                let id = content
                    .get("tool_use_id")
                    .and_then(|i| i.as_str())
                    .ok_or_else(|| anyhow!("Missing tool_use id"))?;
                let name = content
                    .get("name")
                    .and_then(|n| n.as_str())
                    .ok_or_else(|| anyhow!("Missing tool_use name"))?
                    .to_string();

                let input = content
                    .get("input")
                    .ok_or_else(|| anyhow!("Missing tool input"))?
                    .clone();

                let tool_call = CallToolRequestParams::new(name).with_arguments(object(input));
                message = message.with_tool_request(id, Ok(tool_call));
            }
            Some("thinking") => {
                let thinking = content
                    .get("thinking")
                    .and_then(|t| t.as_str())

View on GitHub (pinned to 3810898a74)

Solutions

  1. Log the raw response JSON and inspect the tool_use block for the tool_use_id key and its type
  2. Confirm you are hitting the real Snowflake Cortex endpoint whose tool_use blocks carry tool_use_id
  3. Update fixtures/mocks to include "tool_use_id" as a string on every tool_use block
  4. If upstream genuinely omits ids, skip the malformed block with a warn (see exampleFix) instead of failing the whole message

Example fix

// before
let id = content
    .get("tool_use_id")
    .and_then(|i| i.as_str())
    .ok_or_else(|| anyhow!("Missing tool_use id"))?;

// after - skip malformed block, keep the rest of the response
let id = match content.get("tool_use_id").and_then(|i| i.as_str()) {
    Some(id) => id,
    None => {
        tracing::warn!("skipping tool_use block without tool_use_id");
        continue;
    }
};
Defensive patterns

Strategy: validation

Validate before calling

// validate content_list before response_to_message
for block in content_list {
    if block.get("type").and_then(|t| t.as_str()) == Some("tool_use")
        && block.get("tool_use_id").and_then(|i| i.as_str()).is_none()
    {
        anyhow::bail!("tool_use block without string tool_use_id: {block}");
    }
}

Type guard

fn tool_use_block_is_wellformed(block: &serde_json::Value) -> bool {
    if block.get("type").and_then(|t| t.as_str()) != Some("tool_use") {
        return true;
    }
    block.get("tool_use_id").and_then(|v| v.as_str()).is_some()
}

Try / catch

match response_to_message(&response) {
    Ok(msg) => msg,
    Err(err) if err.to_string().contains("Missing tool_use id") => {
        tracing::warn!("discarding malformed tool_use: {err}");
        Message::assistant()
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: A Snowflake Cortex response whose content_list contains a {"type":"tool_use",...} block without a string tool_use_id field. Typical when the API response shape changes, when a proxy rewrites field names (Anthropic native uses "id" while this parser expects "tool_use_id"), or when the model emits a truncated/malformed tool call.

Common situations: Snowflake Cortex API version drift renaming id to tool_use_id or vice versa; testing against recorded fixtures that use the Anthropic wire format (id) instead of Snowflake's content_list format; responses from a Cortex SQL/function wrapper that drops ids.

Related errors


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