github/copilot-sdk · error · ArgumentException

Tool ' ' received non-object arguments, but its schema does…

Error message

Tool '{tool.Name}' received non-object arguments, but its schema does not define exactly one parameter or one required parameter.

What it means

When a tool is called with non-object (e.g. string, array) arguments, the SDK must map that single value to exactly one tool parameter. If the tool's input schema does not define exactly one parameter — or exactly one required parameter — the SDK cannot decide where the value goes and throws ArgumentException.

Solutions

  1. Pass tool arguments as a JSON object keyed by the schema's property names.
  2. If bare values must be supported, make the tool schema define exactly one parameter (or exactly one required parameter).
  3. Instruct/constrain the model to emit object-form arguments matching the tool's inputSchema.
  4. Catch ArgumentException around tool dispatch and return a descriptive invalid-params error to the caller.

Example fix

// before
await session.CallToolAsync("search", JsonSerializer.SerializeToElement("cats"));

// after
await session.CallToolAsync("search", JsonSerializer.SerializeToElement(new { query = "cats" }));
Defensive patterns

Strategy: validation

Validate before calling

if (args.ValueKind != JsonValueKind.Object &&
    (tool.Schema.Properties?.Count != 1 || tool.Schema.Required?.Count != 1))
    throw new ArgumentException($"Tool '{tool.Name}' requires object arguments");

Type guard

static bool ArgsFitTool(JsonElement args, Tool tool) =>
    args.ValueKind == JsonValueKind.Object ||
    (tool.Schema?.Properties?.Count == 1 && (tool.Schema.Required?.Count ?? 0) <= 1);

Try / catch

try { await session.CallToolAsync(name, args); }
catch (ArgumentException ex) when (ex.Message.Contains("non-object arguments"))
{ return ToolError.InvalidParams(name, ex.Message); }

Prevention

When it happens

Trigger: Calling a tool whose inputSchema has 0 or 2+ parameters (or multiple required ones) while passing arguments that are not a JSON object, e.g. `CallTool("search", "my query")` instead of an object.

Common situations: LLMs emitting bare strings as tool arguments; wrapping a positional-style tool call against an MCP tool with multiple properties; hand-written clients using JSON-RPC positional params for tools with multi-property schemas.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at dotnet/src/Session.cs:1084

                    {
                        if (requiredParameterName is not null)
                        {
                            requiredParameterName = null;
                            break;
                        }

                        requiredParameterName = requiredParameter.GetString();
                    }

                    if (requiredParameterName is not null &&
                        properties.TryGetProperty(requiredParameterName, out _))
                    {
                        return requiredParameterName;
                    }
                }
            }

            throw new ArgumentException(
                $"Tool '{tool.Name}' received non-object arguments, but its schema does not define exactly one parameter or one required parameter.");
        }
    }

    private bool TryClaimExternalTool(string requestId, CancellationTokenSource cancellationSource)
        => ((ICollection<KeyValuePair<string, CancellationTokenSource>>)_pendingExternalTools)
            .Remove(new(requestId, cancellationSource));

    private void CancelExternalTool(string requestId)
    {
        if (!string.IsNullOrEmpty(requestId) && _pendingExternalTools.TryRemove(requestId, out var cancellationSource))
        {
            _ = Task.Run(() =>
            {
                try
                {
                    cancellationSource.Cancel();
                }

View on GitHub (pinned to cd8cf15dc3)