github/copilot-sdk · error

tool parameter schema must be a JSON object

Error message

tool parameter schema must be a JSON object

What it means

tool_parameters converts a JSON Schema object into an ordered parameter map and is the infallible convenience wrapper over try_tool_parameters. It panics via .expect() when the schema is not a JSON object (e.g. an array, string, or null), because a tool parameter schema must be an object per the MCP spec.

Solutions

  1. Use try_tool_parameters and handle the Err instead of panicking on dynamic input
  2. Ensure the schema is a JSON object before calling: verify value.is_object()
  3. Fix the schema construction so the top level is {"type":"object", ...}
  4. Validate schema files/inputs at load time before passing to tool_parameters

Example fix

// before
let params = tool_parameters(loaded_schema); // panics if loaded_schema isn't an object
// after
let params = try_tool_parameters(loaded_schema)
    .expect("tool parameter schema must be a JSON object"); // or handle Err gracefully
Defensive patterns

Strategy: validation

Validate before calling

// Rust
fn safe_tool_parameters(schema: serde_json::Value) -> Option<IndexMap<String, serde_json::Value>> {
    if !schema.is_object() { return None; }
    try_tool_parameters(schema).ok()
}

Type guard

// Rust
fn is_json_object(v: &serde_json::Value) -> bool { v.is_object() }

Prevention

When it happens

Trigger: Calling tool_parameters with a JSON value that is not an object — serde_json::json!(null), a schema read from a file that is an array, or a dynamically built schema that ended up as something other than an object.

Common situations: Loading schemas from untrusted/dynamic sources; typos producing json!(["type","object"]) instead of json!({"type":"object"}); passing Value::Null when a schema is absent.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/7764b1a61ce8d2e6. Report an issue: GitHub.

Appendix: source

Thrown at rust/src/tool.rs:83

/// `serde_json::json!(...)` literal.
///
/// Use [`try_tool_parameters`] when the schema comes from dynamic input and
/// should return a recoverable error instead of panicking.
///
/// # Example
///
/// ```rust
/// use github_copilot_sdk::tool::tool_parameters;
/// use github_copilot_sdk::Tool;
///
/// let mut tool = Tool::default();
/// tool.name = "ping".to_string();
/// tool.description = "ping the server".to_string();
/// tool.parameters = tool_parameters(serde_json::json!({"type": "object"}));
/// # let _ = tool;
/// ```
pub fn tool_parameters(schema: serde_json::Value) -> IndexMap<String, serde_json::Value> {
    try_tool_parameters(schema).expect("tool parameter schema must be a JSON object")
}

/// Fallible variant of [`tool_parameters`] for callers handling dynamic schema input.
pub fn try_tool_parameters(
    schema: serde_json::Value,
) -> Result<IndexMap<String, serde_json::Value>, serde_json::Error> {
    serde_json::from_value(schema)
}

/// Convert an MCP `CallToolResult` JSON value into a Copilot tool result.
///
/// Returns `None` when the value is not shaped like a `CallToolResult`.
pub fn convert_mcp_call_tool_result(value: &serde_json::Value) -> Option<ToolResult> {
    let content = value.get("content")?.as_array()?;
    let mut text_parts = Vec::new();
    let mut binary_results = Vec::new();

    for block in content {

View on GitHub (pinned to cd8cf15dc3)