Codex App sends tool_search_call.arguments as a JSON string, causing Responses API invalid_type errors

Open 💬 4 comments Opened Jul 8, 2026 by u2bo

What version of the Codex App are you using (From “About Codex” dialog)?

Codex App: 26.623.19656.0 (please confirm from About Codex) Codex CLI: 0.142.5

What subscription do you have?

team

What platform is your computer?

_No response_

What issue are you seeing?

Codex App intermittently records/replays internal tool_search_call.arguments as a JSON-encoded string instead of a JSON object. The next Responses API request fails before the assistant can continue.

Observed errors:

Invalid type for 'input[28].arguments': expected an object, but got a string instead.
Invalid type for 'input[62].arguments': expected an object, but got a string instead.

This does not appear to be a chrome-devtools MCP server failure. The chrome-devtools MCP tool list is available:

listed MCP server tools while building tool list server_name=chrome-devtools tool_count=29

But internal tool_search_call items are sometimes malformed.

Examples from local rollout JSONL, with PII redacted:

  1. Session:

019f3f79-5b48-7de1-9f57-0e4179b70a18

Bad input[28]:

{
"type": "tool_search_call",
"call_id": "call_H91Re6731TtEVu1BXFJJ2cuf",
"status": "completed",
"execution": "client",
"arguments": "{\"query\":\"chrome devtools network request list get current page navigate click input\",\"limit\":10}"
}

Expected:

{
"type": "tool_search_call",
"call_id": "call_H91Re6731TtEVu1BXFJJ2cuf",
"status": "completed",
"execution": "client",
"arguments": {
"query": "chrome devtools network request list get current page navigate click input",
"limit": 10
}
}

  1. Same session later:

Bad input[62]:

{
"type": "tool_search_call",
"call_id": "call_Q5wet5GUYIcEiweMrZO8jJjO",
"status": "completed",
"execution": "client",
"arguments": "{\"limit\":10,\"query\":\"spawn agent wait agent subagent multi agent\"}"
}

The local router also logged:

failed to parse tool_search arguments: invalid type: string "...", expected struct SearchToolCallParams

What steps can reproduce the bug?

  1. Start a Codex App thread on Windows.
  2. Use a task that requires Chrome DevTools MCP or asks the model to check available tools.
  3. Example prompt pattern:
  • "Use Chrome DevTools MCP to inspect real page network requests."
  • "Check whether multi-agent/subagent tools are available."
  1. The assistant emits a message such as:

"Chrome DevTools MCP is available... checking whether multi-agent tools are available."

  1. Codex emits an internal tool_search_call.
  2. In some cases, tool_search_call.arguments is stored/sent as a JSON string rather than an object.
  3. The next Responses API request fails with:

Invalid type for 'input[n].arguments': expected an object, but got a string instead.

What is the expected behavior?

tool_search_call.arguments should always be serialized as a JSON object before storing/replaying/sending to Responses API. A malformed internal tool_search item should not poison the thread history.

Additional information

  • This reproduced across multiple sessions:
  • 019f3d04-f96e-76c0-abf4-a04c5b9394cf
  • 019f3f79-5b48-7de1-9f57-0e4179b70a18
  • It can happen without auto-compaction.
  • Auto-compaction may make the issue more visible because malformed history is replayed.
  • Workaround: manually repair the rollout JSONL by converting stringified tool_search_call.arguments to objects and removing orphan parse-failure outputs, or avoid triggering tool_search.

View original on GitHub ↗

4 Comments

u2bo · 1 month ago
codex_fix.zip Man, that JSON stringification bug is a total pain. I ran into the same thing with the CLI and found that the parser was tripping over the double-encoded strings. I put together this little script that forces the object cast before the API request hits.

Is it a virus?

mayank6091 · 1 month ago

@u2bo Bugs around tool invocation are often harder to diagnose than they look because the failure isn't always the serialization itself it can be difficult to reconstruct the exact sequence of reasoning, argument generation, validation, and execution. Having a trace of every tool call with the generated arguments before execution makes these issues much easier to isolate.

u2bo · 1 month ago
@u2bo Bugs around tool invocation are often harder to diagnose than they look because the failure isn't always the serialization itself it can be difficult to reconstruct the exact sequence of reasoning, argument generation, validation, and execution. Having a trace of every tool call with the generated arguments before execution makes these issues much easier to isolate.

We’ve obtained the critical parameters triggering the aforementioned error, which perfectly match the description: tool call generated arguments before execution.

The two latest sets of critical parameters are shown below:

{
  "input_index": 28,
  "type": "tool_search_call",
  "call_id": "call_H91Re6731TtEVu1BXFJJ2cuf",
  "arguments_type": "string",
  "arguments": "{\"query\":\"chrome devtools network request list get current page navigate click input\",\"limit\":10}"
}
{
  "input_index": 62,
  "type": "tool_search_call",
  "call_id": "call_Q5wet5GUYIcEiweMrZO8jJjO",
  "arguments_type": "string",
  "arguments": "{\"limit\":10,\"query\":\"spawn agent wait agent subagent multi agent\"}"
}

These two payloads were generated right before the error occurred. The root cause is straightforward: the field tool_search_call.arguments is expected to be a JSON object, but it is being serialized as an escaped string in practice.

Expected format

"arguments": {
  "query": "spawn agent wait agent subagent multi agent",
  "limit": 10
}

Actual faulty format

"arguments": "{\"limit\":10,\"query\":\"spawn agent wait agent subagent multi agent\"}"

Three layers of local evidence are available for forensic analysis:

  1. rollout*.jsonl: Contains response_item -> tool_search_call records, capturing the raw generated arguments.
  2. logs_2.sqlite, table codex_core::tools::router: Log entries with the error failed to parse tool_search arguments....
  3. logs_2.sqlite, table codex_client::transport: Raw POST JSON payloads sent to /v1/responses, which map input[28] and input[62] to their corresponding response items.
Northern-Summer · 1 month ago

Confirmed locally with additional evidence.

Root cause appears to be mixed-format historical replay:

  • some persisted tool_search_call items store arguments as a JSON string
  • replay path later sends that value back to the Responses API without coercing it to an object
  • the Responses API then rejects input[n].arguments for tool_search_call with invalid_type

We verified a narrow local fix in codex-rs:

  1. On deserialize/read of ResponseItem::ToolSearchCall, if arguments is a stringified JSON object, parse it into a JSON object.
  2. In prompt/history normalization before replay, coerce any in-memory legacy stringified tool_search_call arguments into objects as a defensive fallback.

This dual-layer fix is idempotent and fail-closed:

  • valid objects stay unchanged
  • only stringified JSON objects are coerced
  • invalid or non-object strings are left untouched rather than guessed

Relevant local patch points:

  • protocol/src/models.rs: ToolSearchCall arguments deserialization
  • core/src/context_manager/normalize.rs: prompt replay normalization

Also added regression coverage for:

  • deserializing stringified tool_search_call arguments
  • history normalization before prompt replay