Skip to content

Treat empty json string in tool call as having 'no' arguments.#173

Open
mstancombe wants to merge 1 commit intoanthropics:nextfrom
mstancombe:handle-empty-tool-args
Open

Treat empty json string in tool call as having 'no' arguments.#173
mstancombe wants to merge 1 commit intoanthropics:nextfrom
mstancombe:handle-empty-tool-args

Conversation

@mstancombe
Copy link
Copy Markdown
Contributor

If a non-server tool has no arguments, it's currently still run through the json deserializer, which raises a JsonException.

This doesn't stop the chat client from working, CreateFromParsedArguments just seems to log the exception in the created FunctionCallContent, but it does mean a noisy exception, and the created FunctionCallContent doesn't reflect the true state of play.

This PR handles an empty string as meaning there was no arguments given, which I think is how we should handle this.

@mstancombe mstancombe requested a review from a team as a code owner April 10, 2026 04:38
@sd-st
Copy link
Copy Markdown
Collaborator

sd-st commented Apr 20, 2026

Would you be able to rebase this?

json =>
(Dictionary<string, object?>?)
json => json.Length == 0
? null
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub Does null make sense here or should re be returning an empty dictionary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to expand on my reasoning, as I did look into this a bit before deciding on null, the 'Arguments' on FunctionCallContext class in MEAI looks like this:

/// <summary>
/// Gets or sets the arguments requested to be provided to the function.
/// </summary>
public IDictionary<string, object?>? Arguments { get; set; }

Which doesn't explicitly state when this should be null, but given that it is nullable, I think an llm passing no arguments probably should be interpreted as nothing.

Additionally, the debug view for FunctionCallContext does this:

/// <summary>Gets a string representing this instance to display in the debugger.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay
{
    get
    {
        string display = "FunctionCall = ";

        if (CallId is not null)
        {
            display += $"{CallId}, ";
        }

        display += Arguments is not null ?
            $"{Name}({string.Join(", ", Arguments)})" :
            $"{Name}()";

        return display;
    }
}

If arguments was an empty dict, this would show as 'MyTool()' vs 'MyTool' when it's null. Again, this doesn't prove what the intention was, but I'm guessing (hoping :P) that @stephentoub was expecting a null to represent 'no args'.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, disregard the final point, it would still show MyTool() with no args.. it just would skip the string.join over the empty dictionary, but no output difference.

@mstancombe mstancombe force-pushed the handle-empty-tool-args branch from 2db41e1 to 162ad5b Compare April 20, 2026 23:38
@mstancombe
Copy link
Copy Markdown
Contributor Author

Rebased to latest next.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants