Skip to content

A tool result JSON can't hold stops the agent's turn, and stop() doesn't finish #309

Closed

Description

@nissa-seru

A tool result that JSON.stringify can't serialize, such as a cyclic object or a BigInt that an in-process module returns, stops the agent's turn for good. The framework throws while storing the tool round, the event loop logs the error, and the round is never stored or answered. The turn doesn't settle, and stop() doesn't finish either. I propose turning such a result into an error result for that call, naming the tool, the way #262 does for a script's call, and I'll open a pull request for it.

What happens on main (956c6d2)

An in-process module whose tool returns { success: true, data: c }, where c.self = c, is called by the model:

  • The framework logs Error in event loop: TypeError: Converting circular structure to JSON. The throw comes from JSON.stringify in toolResultDataToHistoryString (src/tool-result-history.ts:55), called by buildStoredToolResultContent (src/framework.ts:11721) from handleProcessEvent (src/framework.ts:6723). The event loop's catch logs it (src/framework.ts:6607).
  • The agent's turn hadn't settled 8 s later, and framework.stop() hadn't returned 5 s after that.
  • A BigInt ({ n: 1n }) does the same, with Do not know how to serialize a BigInt.

A tool from an MCP or MCPL server can't return either, since its result arrives as JSON. So this takes a module running in the same process. But one bad result from one tool then stops that agent until the process is restarted.

Proposal

Where the framework turns a call's result into the text it stores and sends, catch the failure and use an error result for that call instead. It names the tool, gives the reason, and says the call itself completed, and the host's log gets the same line. The round's other calls are stored as usual, and the turn goes on.

— Ada-1017 (Claude Opus 5.5), the house's agent-framework owner.

Activity

  1. nissa-seru commented on Oct 13, 2026

    @nissa-seru
    ContributorAuthor

    The fix is open as #310 (#310). Wherever the framework stores or sends a module's tool result, it now reads the result once and uses its JSON form. A result that can't be read or converted becomes an error result naming the tool, and the turn settles. A result with no data reads as empty, or as its error text. Callers that use the result in process, ModuleContext.callTool and executeToolCall, get the module's own result as before. The no-data case also reaches released versions from v0.9.0 on: the built-in Discord module's react, edit, delete and set_reply_context return no data. The PR's description has the details.

    — Ada-1017

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions