aaif-goose/goose · error

Missing tool input

Error message

Missing tool input

What it means

For a Snowflake Cortex tool_use block, response_to_message requires the input key to be present (any JSON value; it is cloned and passed as tool arguments via object()). Unlike text fields it does not need to be a string, but it must exist. A tool_use block with tool_use_id and name but no input key fails with 'Missing tool input'.

Source

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

                    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())
                    .ok_or_else(|| anyhow!("Missing thinking content"))?;
                let signature = content
                    .get("signature")
                    .and_then(|s| s.as_str())
                    .ok_or_else(|| anyhow!("Missing thinking signature"))?;
                message = message.with_thinking(thinking, signature);
            }
            Some("redacted_thinking") => {
                let data = content

View on GitHub (pinned to 3810898a74)

Solutions

  1. Check the raw block: if "input" is simply absent for a no-arg tool, default it to an empty object on the producer side
  2. Fix fixtures/mocks to always include "input": {} for tool_use blocks
  3. If you control the parser, treat a missing input as empty arguments (see exampleFix) rather than an error

Example fix

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

// after - a tool call without arguments is valid
let input = content.get("input").cloned().unwrap_or_else(|| serde_json::json!({}));
Defensive patterns

Strategy: validation

Validate before calling

for block in content_list {
    if block.get("type").and_then(|t| t.as_str()) == Some("tool_use")
        && !block.get("input").is_some()
    {
        anyhow::bail!("tool_use block without input key: {block}");
    }
}

Type guard

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

Try / catch

match response_to_message(&response) {
    Ok(msg) => msg,
    Err(err) if err.to_string().contains("Missing tool input") => {
        tracing::warn!("tool call without input key dropped: {err}");
        Message::assistant()
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: content_list contains {"type":"tool_use","tool_use_id":"...","name":"..."} with no "input" member. Happens when the model emits a tool call with no arguments and the endpoint omits the key entirely instead of sending "input": {}, or when a proxy strips empty objects.

Common situations: Zero-argument tools (no input) serialized without an input key; strict middlewares that drop empty fields; fixture authors assuming input is optional.

Related errors


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