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

  1. Log/capture the raw Responses API JSON and confirm the function_call item really lacks both call_id and id
  2. If a gateway/proxy (LiteLLM, vLLM, custom) sits in front, upgrade or configure it to forward call_id for function_call output items
  3. Test the same request against the official OpenAI Responses API; if it works there, the bug is in the intermediary, not goose
  4. If you control the payload (fixtures/tests), add a unique call_id (or id) to every function_call item
  5. 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

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


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