hermes - 💡(How to fix) Fix bug: Codex Responses API rejects replayed assistant message items with long id fields

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When resuming or continuing a session that contains assistant messages with codex_message_items, Hermes replays the exact id field from those items into the Responses API input[] array. The Codex backend (chatgpt.com/backend-api/codex) enforces a 64-character maximum on input[N].id, but Codex itself returns ids that are 408 characters long (base64-encoded encrypted blobs). On replay, the API rejects the request with a non-retryable HTTP 400:

Invalid 'input[1].id': string too long. Expected a string with maximum length 64,
but got a string with length 408 instead.

This makes any session that received a long message id permanently unrecoverable when using the openai-codex provider — every subsequent API call replays the same invalid id and gets the same 400.

Error Message

2026-05-16 20:08:44,080 WARNING run_agent: API call failed (attempt 1/3) error_type=BadRequestError provider=openai-codex model=gpt-5.5 summary=HTTP 400: Invalid 'input[1].id': string too long. Expected a string with maximum length 64, but got a string with length 408 instead.

2026-05-16 20:08:44,097 ERROR root: Non-retryable client error: Error code: 400

Root Cause

Three locations in agent/codex_responses_adapter.py pass through the message id without any length validation:

Fix Action

Fix / Workaround

  • Severity: High — makes sessions permanently unrecoverable on the openai-codex provider
  • Scope: Any session using openai-codex / Codex backend that gets a phase-tagged assistant response (common with gpt-5.x models)
  • Workaround: Start a new session (/new). There is no way to recover the broken session without manually editing the session database to remove the codex_message_items with long ids.
  • Additional note: The Skipping session persistence for large failed session to prevent growth loop message in the error output suggests Hermes already detects the loop but has no way to break it — the corrupt history is already persisted from the previous successful turn.

Code Example

Invalid 'input[1].id': string too long. Expected a string with maximum length 64,
but got a string with length 408 instead.

---

item_id = getattr(item, "id", None)
if isinstance(item_id, str) and item_id:
    raw_message_item["id"] = item_id  # No length check

---

item_id = raw_item.get("id")
if isinstance(item_id, str) and item_id.strip():
    replay_item["id"] = item_id.strip()  # No length check

---

item_id = item.get("id")
if isinstance(item_id, str) and item_id.strip():
    normalized_item["id"] = item_id.strip()  # No length check

---

{
  "type": "message",
  "role": "assistant",
  "status": "completed",
  "content": [{"type": "output_text", "text": "Sí.\n\nSi peta, tráeme esto..."}],
  "id": "MwE5uYA3ttWKFElTqaMpoQPCcw+ehswAgZvnxpewA1b...w3Q=",
  "phase": "final_answer"
}

---

2026-05-16 20:08:44,080 WARNING run_agent: API call failed (attempt 1/3)
  error_type=BadRequestError provider=openai-codex model=gpt-5.5
  summary=HTTP 400: Invalid 'input[1].id': string too long.
  Expected a string with maximum length 64, but got a string with length 408 instead.

2026-05-16 20:08:44,097 ERROR root: Non-retryable client error: Error code: 400

---

# Line 331-333: change to omit id entirely (like reasoning items do)
# replay_item["id"] = item_id.strip()  # REMOVE

---

MAX_RESPONSES_ITEM_ID_LENGTH = 64

item_id = raw_item.get("id")
if isinstance(item_id, str) and item_id.strip():
    stripped = item_id.strip()
    if len(stripped) <= MAX_RESPONSES_ITEM_ID_LENGTH:
        replay_item["id"] = stripped
RAW_BUFFERClick to expand / collapse

Bug: Codex Responses API rejects replayed assistant message items with long id fields

Summary

When resuming or continuing a session that contains assistant messages with codex_message_items, Hermes replays the exact id field from those items into the Responses API input[] array. The Codex backend (chatgpt.com/backend-api/codex) enforces a 64-character maximum on input[N].id, but Codex itself returns ids that are 408 characters long (base64-encoded encrypted blobs). On replay, the API rejects the request with a non-retryable HTTP 400:

Invalid 'input[1].id': string too long. Expected a string with maximum length 64,
but got a string with length 408 instead.

This makes any session that received a long message id permanently unrecoverable when using the openai-codex provider — every subsequent API call replays the same invalid id and gets the same 400.

Environment

  • Hermes version: v0.13.0 (v2026.5.7), commit 8298782c9
  • Provider: openai-codex
  • Endpoint: https://chatgpt.com/backend-api/codex
  • Model: gpt-5.5
  • API mode: codex_responses
  • OS: Linux 6.17.0-19-generic

Steps to Reproduce

  1. Start a session using openai-codex provider with any gpt-5.x model
  2. Get at least one assistant response that includes a phase (e.g. final_answer) — this triggers codex_message_items capture with the server-assigned id
  3. Continue the conversation (or resume the session later)
  4. On the next API call, Hermes replays the previous assistant message item including its id field
  5. The Codex backend rejects it with HTTP 400

Root Cause

Three locations in agent/codex_responses_adapter.py pass through the message id without any length validation:

1. Response capture (_normalize_codex_response, line ~913-915)

The server-assigned id (408-char base64 blob) is captured verbatim into codex_message_items:

item_id = getattr(item, "id", None)
if isinstance(item_id, str) and item_id:
    raw_message_item["id"] = item_id  # No length check

2. Replay conversion (_chat_messages_to_responses_input, line ~331-333)

When building the input for the next API call, the stored id is replayed as-is:

item_id = raw_item.get("id")
if isinstance(item_id, str) and item_id.strip():
    replay_item["id"] = item_id.strip()  # No length check

3. Preflight validation (_preflight_codex_input_items, line ~585-587)

The preflight pass that validates and normalizes input items also passes through message ids without length validation:

item_id = item.get("id")
if isinstance(item_id, str) and item_id.strip():
    normalized_item["id"] = item_id.strip()  # No length check

Contrast with function_call items: the preflight already strips the optional id field from function_call items (only call_id is preserved), and there are tests for this (test_preflight_codex_api_kwargs_strips_optional_function_call_id). No equivalent protection exists for message items.

Contrast with reasoning items: reasoning items explicitly strip the id field before replay (line ~286: replay_item = {k: v for k, v in ri.items() if k != "id"}), with a comment explaining that store=False makes server-side id lookup fail with 404. The same rationale applies to message item ids.

Evidence

Request dump (automatically saved by Hermes on failed requests)

File: ~/.hermes/sessions/request_dump_20260516_200806_608e64_20260516_200844_085227.json

The offending input item at index 1:

{
  "type": "message",
  "role": "assistant",
  "status": "completed",
  "content": [{"type": "output_text", "text": "Sí.\n\nSi peta, tráeme esto..."}],
  "id": "MwE5uYA3ttWKFElTqaMpoQPCcw+ehswAgZvnxpewA1b...w3Q=",
  "phase": "final_answer"
}
  • id length: 408 characters
  • API limit: 64 characters

Agent log excerpt

2026-05-16 20:08:44,080 WARNING run_agent: API call failed (attempt 1/3)
  error_type=BadRequestError provider=openai-codex model=gpt-5.5
  summary=HTTP 400: Invalid 'input[1].id': string too long.
  Expected a string with maximum length 64, but got a string with length 408 instead.

2026-05-16 20:08:44,097 ERROR root: Non-retryable client error: Error code: 400

Persistence across sessions

The same 408-char id appears in codex_message_items of multiple session files (the message was from an early turn that got carried into subsequent sessions via --continue/--resume):

  • session_20260515_212106_3cc835.json
  • session_20260516_012457_d84ddf.json
  • session_20260516_100602_b3c002.json
  • session_20260516_102140_7df31e.json
  • (and more)

Suggested Fix

Option A (minimal — strip id on replay, same as reasoning items):

In _chat_messages_to_responses_input, don't include id in replayed message items. The comment on reasoning items already explains why: with store=False (Hermes's default), the API cannot look up items by id server-side. The phase field is what matters for cache performance:

# Line 331-333: change to omit id entirely (like reasoning items do)
# replay_item["id"] = item_id.strip()  # REMOVE

Option B (defensive — truncate/drop oversized ids at all three sites):

Add a length guard wherever message item ids are set:

MAX_RESPONSES_ITEM_ID_LENGTH = 64

item_id = raw_item.get("id")
if isinstance(item_id, str) and item_id.strip():
    stripped = item_id.strip()
    if len(stripped) <= MAX_RESPONSES_ITEM_ID_LENGTH:
        replay_item["id"] = stripped

Apply at all three locations (_normalize, _chat_messages_to_responses_input, _preflight).

Option C (belt and suspenders — both A and B):

Strip id on replay (Option A) AND add the length guard in preflight (Option B) as a safety net for any other code path that might inject oversized ids.

I'd recommend Option A since it's consistent with the existing reasoning-item approach and the store=False semantics make the id useless for server-side lookups anyway.

Impact

  • Severity: High — makes sessions permanently unrecoverable on the openai-codex provider
  • Scope: Any session using openai-codex / Codex backend that gets a phase-tagged assistant response (common with gpt-5.x models)
  • Workaround: Start a new session (/new). There is no way to recover the broken session without manually editing the session database to remove the codex_message_items with long ids.
  • Additional note: The Skipping session persistence for large failed session to prevent growth loop message in the error output suggests Hermes already detects the loop but has no way to break it — the corrupt history is already persisted from the previous successful turn.

Related

  • Existing test test_preflight_codex_api_kwargs_strips_optional_function_call_id shows awareness of the id-stripping pattern for function_call items — message items need the same treatment
  • The reasoning item replay code (line ~282-286) already strips id with an explicit comment about store=False — message items should follow the same pattern
  • Compression summary also fails in this session (google/gemini-3-flash-preview not supported on Codex), but that's a separate issue

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix bug: Codex Responses API rejects replayed assistant message items with long id fields