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 = contentView on GitHub (pinned to 3810898a74)
Solutions
- Check the raw block: if "input" is simply absent for a no-arg tool, default it to an empty object on the producer side
- Fix fixtures/mocks to always include "input": {} for tool_use blocks
- 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
- Serialize zero-argument tool calls as "input": {} — never omit the key
- Disable middleware that strips empty JSON fields on the response path
- Keep fixtures byte-faithful to real Cortex output
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
- Missing tool_use id
- Missing tool_use name
- Missing thinking content
- Missing thinking signature
- Missing redacted_thinking data
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/ef9dcf63e8e8c519.
Report an issue: GitHub.