Codex CLI persists malformed tool_search_call arguments, causing 400 property_name_above_max_length on resume

Open 💬 12 comments Opened Jun 4, 2026 by volveezz

What version of Codex CLI is running?

0.137.0

What subscription do you have?

Pro

Which model were you using?

gpt-5.5

What platform is your computer?

Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 unknown

What terminal emulator and version are you using (if applicable)?

WSL

What issue are you seeing?

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "[ObjectParam] [input[55].arguments.>'+ennialsf\u00f3lnub etsennialsreti\u05e2\u05e0\u05d8\u05e9apie \u0440\u044b\u0448iv\u00e9playerschuheNegotiishe \u0906\u092f\u094b\u091c
Tapelade\u0639\u062f\u064a\u0646ta\u00e7\u00e3onubwriter/cop HarrietenciamentobotsJuan meigeria rezSED\u0430\u043b\u0430\u043croch pencilsretipleados\u0434\u0435\u043d\u0438\u0435\u043c Nyereit Bots Bulk
Pantryasinoi\u00e3o.Generic_fillfinder\u043d\u0443\u0446\u044c Rasullaug\u043b\u0435\u0439_STAolfasetreit Streit Nou Dee Preston/legast\u00e1naFABj\u00e4nendenteJason Argentbots ETS opts th\u01b0\u1edfng
ballot frustratingnthestrian uncertainties eyeb Kerryhtarething\u6765\u770bbotAllocatornyamaWrite\u05dc\u05d9\u05eanub\u043f\u043b\u044f sach.zaxxer\u043f\u0430\u0440\u0430\u0442/be TachPur \u9999\u6e2fLaugh
konkurr el\u00e9trACLEolf globaleenant_ls\u10d5\u10d8\u10d3 setsreit leastelassen\u00fcssURITY \u0645\u0639\u0646gahretilish Ball\u2019abandil\u00f3st\u00e6Substring\u0941\u092anub \u0440\u0430\u0434\u0443
interferlanders\u0441\u0435\u043d\u0434\u0456 s\u00f8ger \u5927\u53d1\u5feb\u4e09\u5b98\u7f51 j\u00e9g\u00ebriszendstylesbotu cari hinkwaswo\u0443\u0441\u044b\u043b\u0430\u0440\u04b5\u04d9vidorsections/
Subthreshold\u0525\u0448\u044cdaawriteatillugu \u5927\u53d1\u5feb\u4e09\u5982\u4f55\u0445\u044b\u04b5
\u04bb\u04d9\u0440\u0431\u0438\u0439retry_Botsthroughoutfilebancookies\u04bf\u043a\u0430itaatspu\u0430\u04e1\u0430\u0440\u0430 Bruins\u5f69\u7968\u7f51\u5740pythonrit] [property_name_above_max_length] Invalid
property name in 'input[55].arguments': '>'+...rit' is too long. Expected a string with maximum length 256, but got a string with length 806 instead."
  },
  "status": 400
}

What steps can reproduce the bug?

Uploaded thread: 019e9232-51e7-7051-89ac-9fca142e3b3d

What is the expected behavior?

_No response_

Additional information

_No response_

View original on GitHub ↗

12 Comments

YitzhakMizrahi · 2 months ago

I hit the same failure class and was able to recover the affected session by editing the persisted rollout JSONL. Adding details in case they help narrow the bug.

Environment:

  • Current Codex CLI: 0.137.0
  • Affected session was originally created with Codex CLI: 0.130.0
  • The issue persisted after upgrading to 0.137.0, so upgrading alone did not repair the affected session.
  • Platform: Fedora Linux 44, kernel 7.0.9-205.fc44.x86_64
  • Terminal/multiplexer: tmux 3.6a
  • Model/provider: OpenAI, gpt-5.5

Observed behavior:

  • codex resume <thread-id> failed repeatedly with an API 400 shaped like:
[ObjectParam] [input[91].arguments.<garbage>] [property_name_above_max_length]
Invalid property name in 'input[91].arguments': ... is too long.
Expected a string with maximum length 256, but got a string with length 708 instead.
  • New Codex sessions worked normally; only the affected persisted session failed.

Local transcript diagnosis:

  • The affected rollout JSONL contained malformed records of type response_item / tool_search_call.
  • In those records, .payload.arguments was an object with a single garbage property key instead of the expected argument shape.
  • I found two malformed tool_search_call records in the same rollout file:
  • one oversized key length was 708
  • another oversized key length was 566
  • Each malformed tool_search_call was followed by a function_call_output with:
failed to parse tool_search arguments: missing field `query`

Recovery that worked:

  1. Backed up the rollout JSONL.
  2. Removed the malformed tool_search_call record.
  3. Removed its paired failed function_call_output record.
  4. Removed the adjacent token_count event for that failed tool call.
  5. Re-scanned the JSONL for any remaining .payload.arguments object keys longer than 256 chars.
  6. Found and removed a second malformed tool_search_call block the same way.
  7. After both malformed blocks were removed, the session resumed successfully.

The practical jq diagnostic I used was:

jq -r '
  select(.payload? and (.payload.arguments? | type)=="object") as $row
  | $row.payload.arguments
  | to_entries[]
  | select((.key|length) > 256)
  | [input_line_number, $row.timestamp, $row.type, ($row.payload.type // ""), ($row.payload.call_id // ""), (.key|length), (.key[0:80])]
  | @tsv
' ~/.codex/sessions/**/rollout-*.jsonl

Hypothesis:

Codex can persist malformed tool_search_call.payload.arguments after tool argument parsing fails. On resume, the persisted transcript is replayed into the API request, and the API rejects the oversized object key before the model can continue. The resume path probably needs to sanitize/drop invalid historical tool calls, or the persistence path should avoid writing malformed tool-call items after argument parsing fails.

menicPL · 2 months ago

I hit the same failure class in Codex Desktop, not just CLI resume.

Observed behavior:

  • Instead of a normal assistant reply, Codex surfaced the raw API error JSON directly in chat.
  • The error shape was:
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "[ObjectParam] [input[293].arguments....] [property_name_above_max_length] Invalid property name in 'input[293].arguments': ... Expected a string with maximum length 256, but got a string with length 1709 instead."
  },
  "status": 400
}

Notable details:

  • failing path: input[293].arguments
  • oversized property-name length: 1709
  • the garbage key contained long multilingual/random-looking text, similar to the malformed tool-call cases already described here
  • from the user's perspective this looked like Codex itself broke and dumped transport/backend error JSON into the conversation instead of recovering or showing a friendlier failure

This suggests the bug is broader than just a CLI resume edge-case:

  1. malformed tool-call/session state can still be constructed in Desktop
  2. the app may expose the backend validation error directly to the user instead of handling it as an internal failure

If helpful, I can add more reproduction context later, but I wanted to confirm this exact property_name_above_max_length pattern is also happening in Desktop with input[293].arguments.

ken-jo · 2 months ago

I hit the same failure class on Codex CLI and was able to narrow one occurrence down to the raw Responses websocket event, before rollout JSONL replay.

Environment:

  • Codex CLI: 0.139.0
  • Model: gpt-5.5
  • Platform: Linux 6.17.0-1016-oracle on aarch64
  • Originator in telemetry: codex-tui

What I observed:

  • The affected rollout had 40 tool_search_call response items.
  • 39 had the expected argument shape with query / limit.
  • 1 had malformed arguments:
{
  "type": "tool_search_call",
  "call_id": "call_0arIzewv9N3HEFLA8hQMNZ5o",
  "status": "completed",
  "execution": "client",
  "arguments": {
    "<redacted generated multilingual/gibberish key, 2503 chars / 2910 UTF-8 bytes>": []
  }
}

The immediately following rollout item was:

{
  "type": "function_call_output",
  "call_id": "",
  "output": "failed to parse tool_search arguments: missing field `query`"
}

Additional evidence from ~/.codex/logs_2.sqlite:

  • The malformed payload was already present in the raw websocket trace, not introduced by the JSONL writer.
  • Sequence:
  • 2026-06-16T09:04:39Z: stream disconnected - retrying sampling request (3/5 in 733ms)...
  • 2026-06-16T09:04:41Z: websocket reconnect succeeded for /responses
  • 2026-06-16T09:04:49Z: raw websocket event response.output_item.added for the same item/call id:
{
  "type": "response.output_item.added",
  "item": {
    "id": "tsc_0f0cd4c506096518016a3111b1dcb081989865875883ca83ab",
    "type": "tool_search_call",
    "status": "in_progress",
    "arguments": {},
    "call_id": "call_0arIzewv9N3HEFLA8hQMNZ5o",
    "execution": "client"
  },
  "sequence_number": 2
}
  • 2026-06-16T09:05:30Z: raw websocket event response.output_item.done for the same item/call id contained the malformed 2503-character property name in arguments.
  • The router then logged:
handle_output_item_done:build_tool_call: error=failed to parse tool_search arguments: missing field `query`

Code path that matches the transcript:

  • codex-rs/protocol/src/models.rs: ResponseItem::ToolSearchCall.arguments is a raw serde_json::Value.
  • codex-rs/core/src/tools/router.rs: ToolRouter::build_tool_call deserializes that value into SearchToolCallParams { query, limit }, which fails with missing field query.
  • codex-rs/core/src/stream_events_utils.rs: the FunctionCallError::RespondToModel path records a generic function_call_output with call_id: String::new().
  • After that, history normalization logs Tool search output is missing for call id: call_0arIzewv9N3HEFLA8hQMNZ5o and synthesizes an empty tool_search_output on later requests.

My current hypothesis:

  • The original malformed argument object is produced upstream of ToolRouter::build_tool_call because the completed Responses websocket item already contains it.
  • The client-side bug that makes this harder to recover from is that malformed client-executed tool_search_call parse failures are recorded as an orphan function_call_output with an empty call_id instead of a tool_search_output tied to the original call_id.
  • A robust fix likely needs two parts:
  1. server/model-side prevention of malformed tool_search_call.arguments, and
  2. client-side hardening so invalid tool_search_call items preserve call_id and do not poison persisted history/resume.

Potential client-side fix shape, if maintainers agree:

  • Add a regression test for a client tool_search_call with invalid arguments.
  • When parsing fails before a ToolCall is built, preserve the original call_id and record/respond with a tool_search_output such as tools: [] (or another structured error shape the Responses API accepts for tool_search) instead of function_call_output { call_id: "" }.
  • Consider a history normalization/resume guard that drops or repairs historical client tool_search_call items whose arguments are not shaped like SearchToolCallParams, to avoid later property_name_above_max_length replay failures.
ken-jo · 2 months ago

I have a small client-side hardening patch prepared for the second part above: it preserves the original tool_search_call.call_id and records a tool_search_output instead of an orphan function_call_output { call_id: "" } when client tool_search argument parsing fails.

The local patch is intentionally narrow:

  • 2 Rust files changed
  • regression test for malformed tool_search_call.arguments
  • git diff --check: pass
  • cargo fmt -- --config imports_granularity=Item --check: pass
  • just test -p codex-core handle_output_item_done_preserves_tool_search_call_id_for_invalid_arguments: pass

If this direction matches what the Codex team wants, would you be open to inviting an external PR for this focused client-side fix? I will hold off on opening a PR unless invited, per docs/contributing.md.

Necmttn · 2 months ago

Resume should validate persisted tool-call envelopes before replay. If a tool_search_call has malformed arguments, keep the original call id, attach a parse_error record, and emit a matching tool_search_output or repairable skip event. The session should never serialize an orphan output with an empty call id and then fail the whole resume request.

---

_Generated with ax._

RaresKeY · 2 months ago

I hit what looks like the same issue on Codex CLI 0.142.3 using gpt-5.5 with xhigh reasoning.

Sanitized sequence from local logs:

  1. A long CLI session triggered context compaction.
  2. After compaction, the session recorded a tool_search_call whose arguments object did not contain the required query field.
  3. Instead, arguments contained a single generated/gibberish multilingual property name with length 6585, value 173.
  4. The next transcript item recorded a failed tool parse with an empty call_id:
failed to parse tool_search arguments: missing field `query`
  1. On the next user turn, session replay sent the malformed item back in the request and the API rejected the whole turn:
invalid_request_error
[property_name_above_max_length]
Invalid property name in 'input[54].arguments': expected maximum length 256, got length 6585

I also saw normalization warnings consistent with poisoned replay state:

Tool search output is missing for call id: <redacted>
Orphan function call output for call id:

This was Codex CLI, not Desktop. Auth/websocket connection appeared healthy, and continuing in a fresh thread worked. The issue seems to be that an invalid internal tool_search_call can be persisted after compaction and then poisons future session replay.

TidyWeb · 1 month ago

For those still hitting tool-calling issues (namespace doubling, broken HTML rendering): before rolling back to 0.142.5, check whether your install is actually complete.

My symptoms matched what's being reported here, but the root cause turned out to be a missing platform binary — @openai/codex-linux-x64 — not a version regression. The global npm install -g had silently skipped the optional dependency, leaving an incomplete install regardless of version.

Fix (Linux x64):

npm install -g @openai/codex@0.143.0
cd ~/.nvm/versions/node/$(node -v)/lib/node_modules/@openai/codex
npm install --include=optional

That installs codex-linux-x64 into the correct nested location. Currently running 0.143.0 without issue — tool calls passing, HTML rendering correctly.

krashidov · 1 month ago

Independent confirmation from an application embedding @openai/codex-sdk / Codex 0.143.0.

The failure sequence matches this issue:

  1. Codex emitted and persisted a response_item whose payload type was tool_search_call.
  2. The payload's arguments object contained a generated property name longer than 256 characters (985 characters in the observed case).
  3. The original turn was allowed to finish and the rollout was persisted.
  4. On the next resume, the persisted item was replayed as Responses API input.
  5. OpenAI rejected the turn before model output with 400 invalid_request_error, property_name_above_max_length, at input[36].arguments.

The important inconsistency is that Codex can emit/persist a response item that the API will not accept when that same item is replayed as input. This permanently poisons resume for the session unless the client validates the rollout or discards/repairs the invalid item.

A sanitized downstream workaround is to:

  • validate persisted tool-call argument property names before restoring a rollout;
  • treat this exact provider rejection as a resume failure;
  • start a fresh session with reconstructed full conversation history.

Expected upstream behavior: Codex should not persist unreplayable tool-call items, or the resume path should validate/sanitize them before sending history back to the API. No malformed property name or customer/session identifiers are included here.

dannysauer · 1 month ago

Independent confirmation with a newer CLI and a remote-compaction failure mode.

Environment:

  • Linux x86_64
  • original thread created by Codex CLI 0.144.6
  • fork created by Codex CLI 0.145.0
  • model gpt-5.5, reasoning xhigh
  • ChatGPT authentication, remote_compaction_v2 enabled

The original rollout contains two malformed tool_search_call response items. Each arguments object has a single generated multilingual/gibberish property key instead of query; the key lengths are 538 and 1082. The calls were persisted with call IDs, while the following parse-failure outputs have empty call IDs.

After that, every prompt emitted a remote compaction 400 at input[294].arguments with property_name_above_max_length (maximum 256, observed 538). Forking the affected thread did not recover it: the new 0.145.0 fork inherited the poisoned history and failed the same way. A fresh thread with a manual handoff works.

Additional data points:

  • malformed calls occurred at 2026-07-25T20:03:20Z and 2026-07-25T20:04:42Z
  • original thread: 019f76b7-afbe-7fe3-b4f5-492eb44ef059
  • poisoned fork: 019f9af1-f0f3-7c92-aa41-e132c8765f4c

No prompt or repository content is included here. This confirms that upgrading/forking does not sanitize the invalid historical item and that it can permanently break automatic remote compaction, not only explicit resume.

kangsive · 18 days ago

Same failure class here, and I can add two things this thread is missing: it reproduces on the first-party Responses API with no gateway in front of it, and I have per-model rates from a production dataset showing it is ~40x worse on gpt-5.6 than gpt-5.5.

Environment

  • Endpoint: https://api.openai.com/v1/responses — first-party, no proxy, no Azure/Bedrock
  • Models: gpt-5.6-terra, gpt-5.6-sol
  • Streaming, function tools with strict: true, reasoning effort medium/high
  • Long agentic sessions, 100+ tool calls per conversation

Variant 1: arguments exceed the limit the same API enforces

A single tool call was emitted with 1,126,633 characters of arguments — one array element repeated tens of thousands of times:

400 invalid_request_error
Invalid 'input[167].arguments': string too long. Expected a string with
maximum length 1048576, but got a string with length 1126633 instead.
code: string_above_max_length

The core issue, same as this ticket: the model emits an arguments string that the same API then refuses to accept. Generation and validation disagree.

And as reported here, it is unrecoverable rather than transient. The output is persisted as conversation history, so every later request in that conversation replays it and gets the identical 400. Three such calls landed in one conversation (1,126,633 / 1,062,179 / 751,153 chars) and it could never make progress again. Recovery is impossible client-side without rewriting the model's own recorded output.

Variant 2: chat-template scaffolding and glitch tokens inside arguments

This is the part that matches the "garbled Unicode and mixed-language text" in the original report, and it looks like more than encoding noise. A 32,603-char call contained, inside the arguments JSON:

  • to=functions.glob, to=functions.read_file, ChatGPT to=functions.glob — channel/routing syntax appearing as argument content
  • <|DELIM_nT5|>, 】【。system, 】【。assistant, +#+#+#+#+#+assistant
  • o200k undertrained/spam tokens: 彩神争霸, 天天中彩票, plus Thai, Armenian and Georgian fragments
  • the model's own reasoning text, verbatim: "Oops malformed. Need resolve.", "Why tool call formatting malfunction?", "There is serious attempted faulty tool calls? Need actual."

The model appears to notice its output is malformed and attempts to recover by re-emitting tool calls inline as text, which extends the corruption instead of ending it.

This variant is usually under the size limit, so it parses and executes. One built a find with 815 -name clauses and returned normally. No error surfaced anywhere — silent corruption, which is worse than the 400.

Measured incidence

Every tool call in our production database, matched on the markers above:

| Model | Tool calls | Degenerate | Rate | First seen |
|---|---:|---:|---:|---|
| gpt-5.6-terra | 5,035 | 45 | 0.89% | 14 Jul 2026 |
| gpt-5.6-sol | 592 | 4 | 0.68% | 20 Jul 2026 |
| gpt-5.5 | 9,533 | 2 | 0.021% | 15 Jun 2026 |
| gpt-5.4 | 14,936 | 1 | 0.007% | 14 May 2026 |

It was present from our first day of using gpt-5.6-terra (14 Jul 2026), which suggests it is inherent to the release rather than a later regression.

Caveats, so nobody over-reads the numbers: this is not a controlled comparison — reasoning-effort mix and prompts varied over the period — and events cluster per conversation (27 of the 45 came from one conversation), so the rate is not an independent per-call probability. Once a conversation degenerates it tends to keep degenerating. The directional gap is large enough that volume alone does not explain it, and 4,383 calls on another vendor's model with zero hits is a useful null.

shleder · 14 days ago

The persisted tool_search_call.arguments corruption here is a strong recovery test: the rollout remains readable, but replay is permanently rejected because a malformed property name was saved into history.

Codex Rescue 0.1.0a3 has proven handling for some corrupted tool-call metadata, but not arbitrary argument-shape failures. I’m trying to validate whether its current diagnostic boundary catches this safely. It will not delete those records, guess the intended query, or modify the original rollout; recovery remains fail-closed.

If you still have a pre-edit copy, would you try:

pipx install codex-rescue==0.1.0a3
codex-rescue sessions
codex-rescue doctor --latest

Sanitized output is sufficient—please don’t share the raw JSONL, prompts, repository contents, DBs, credentials, or private paths.

https://github.com/shleder/codex-rescue

dannysauer · 8 days ago

I hit the same failure mode in a long-running Codex chat using codex-cli 0.147.0 on Linux x86_64. Remote compaction returned HTTP 400 with property_name_above_max_length for input[294].arguments: the malformed property name was 538 characters (maximum 256) and consisted of apparently generated multilingual/random tokens. After the first occurrence, every subsequent prompt in that thread emitted the same remote compact error, so the thread could not recover in place. Moving the task to a new chat via a handoff restored normal operation. I am not attaching the original thread because it contains private infrastructure context, but the stable details are: remote compact task, status 400, input[294].arguments, expected max 256, observed 538. This appears consistent with malformed tool-call arguments being persisted into the compaction input rather than a user-authored property name.