aaif-goose/goose · error
Responses function_call output missing call_id and id
Error message
Responses function_call output missing call_id and id
What it means
Goose converts a non-streaming OpenAI Responses API payload (ResponsesApiResponse) into its internal Message format via responses_api_to_message. For every output item of type function_call it needs an identifier to correlate the tool request with the later tool result: it prefers call_id and falls back to id. When the model/gateway returns a function_call item where both call_id and id are null or absent, the whole response conversion aborts with this error.
Source
Thrown at crates/goose-provider-types/src/formats/openai_responses.rs:850
id,
Ok(CallToolRequestParams::new(strip_unicode_tags(name))
.with_arguments(object(sanitize_tool_arguments(
input.clone(),
)?))),
));
}
}
}
}
ResponseOutputItem::FunctionCall {
id,
call_id,
name,
arguments,
..
} => {
let request_id = call_id.clone().or_else(|| id.clone()).ok_or_else(|| {
anyhow!("Responses function_call output missing call_id and id")
})?;
let request_id = sanitize_tool_request_id(&request_id, &mut tool_request_ids)?;
let parsed_args = parse_tool_arguments(arguments)?;
content.push(MessageContentBlock::tool_request(
request_id,
Ok(CallToolRequestParams::new(strip_unicode_tags(name))
.with_arguments(object(parsed_args))),
));
}
}
}
let mut message = Message::new(Role::Assistant, chrono::Utc::now().timestamp(), content);
message = message.with_id(response.id.clone());
Ok(message)View on GitHub (pinned to 3810898a74)
Solutions
- Log/capture the raw Responses API JSON and confirm the function_call item really lacks both call_id and id
- If a gateway/proxy (LiteLLM, vLLM, custom) sits in front, upgrade or configure it to forward call_id for function_call output items
- Test the same request against the official OpenAI Responses API; if it works there, the bug is in the intermediary, not goose
- If you control the payload (fixtures/tests), add a unique call_id (or id) to every function_call item
- Last resort for unfixable upstreams: patch this match arm to synthesize a fallback id (e.g. format!("call_{n}") from tool_request_ids.len()) instead of failing the entire response
Example fix
// before
let request_id = call_id.clone().or_else(|| id.clone()).ok_or_else(|| {
anyhow!("Responses function_call output missing call_id and id")
})?;
// after - degrade gracefully instead of dropping the whole response
let request_id = call_id.clone().or_else(|| id.clone()).unwrap_or_else(|| {
tracing::warn!("function_call output missing call_id and id; synthesizing fallback");
format!("call_{}", tool_request_ids.len())
}); Defensive patterns
Strategy: validation
Validate before calling
// before calling responses_api_to_message
let bad = response
.output
.iter()
.filter(|item| matches!(item, ResponseOutputItem::FunctionCall { id: None, call_id: None, .. }))
.count();
if bad > 0 {
anyhow::bail!("refusing to convert: {bad} function_call item(s) without call_id/id");
} Type guard
fn function_call_has_identifier(item: &ResponseOutputItem) -> bool {
match item {
ResponseOutputItem::FunctionCall { id, call_id, .. } => id.is_some() || call_id.is_some(),
_ => true,
}
} Try / catch
match responses_api_to_message(&response) {
Ok(msg) => msg,
Err(err) if err.to_string().contains("missing call_id and id") => {
tracing::warn!("dropping response with unidentifiable tool calls: {err}");
Message::assistant()
}
Err(err) => return Err(err),
} Prevention
- Prefer the official OpenAI Responses endpoint or a gateway verified to forward call_id on function_call items
- Log raw Responses payloads (debug level) so id-stripping intermediaries are identifiable immediately
- Keep recorded fixtures faithful: never strip call_id/id when anonymizing transcripts
- Add a unit test asserting every function_call fixture carries an identifier
When it happens
Trigger: A provider configured with the openaiResponses format returns a tool call: response.output contains {"type":"function_call","name":...,"arguments":...} with neither call_id nor id. Typical with OpenAI Responses-compatible gateways (LiteLLM, vLLM, Azure proxies) or hand-built JSON fixtures that omit these fields; responses_api_to_message is the non-streaming path, so this fires on a completed request, not a stream chunk.
Common situations: Switching a provider from chat completions to the Responses API schema of a third-party gateway that drops call_id; upstream API schema drift after an OpenAI or proxy version bump; replaying recorded responses where the id fields were stripped; unit tests with hand-written response fixtures missing the ids.
Related errors
- Missing tool_use id
- Missing tool_use name
- Missing tool input
- Invalid model spec '{}': expected format 'user/repo:quantiza
- Cannot parse shard total from '{}'
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/851421067ce6c7b2.
Report an issue: GitHub.