Skip to content

Unbounded image history payload can make threads unrecoverable with 413 #30441

Open

Description

@DJNgoma

What version of Codex are you using?

Current main as of commit bdd282f in openai/codex.

What issue are you seeing?

After asking Codex to analyze an image for OCR, the thread can become permanently stuck with repeated request-size failures:

unexpected status 413 Payload Too Large: request too large, url: http://127.0.0.1:48111/v1/responses

Once the thread is in this state, sending a follow-up message still fails even when the image attachment is no longer attached to the chat. The oversized payload appears to be persisted in conversation history/rollout state and replayed on resume.

This looks related to #11440, but the concrete path here is image/tool-output history rather than only long text compaction.

Root cause hypothesis

In the current code path, image tool/function output can be prepared into a base64 data URL before being persisted into conversation history. Resume reconstruction later records persisted rollouts into history, and function output image items are preserved without counting the image URL against truncation budgets.

That means one large prepared image data URL can poison a rollout/history record and keep being included in later /v1/responses payloads.

Expected behavior

Codex should bound image payloads that enter model-visible history and should recover by replacing or omitting oversized image items, rather than replaying an unbounded base64 image until every follow-up request fails with 413.

Proposed fix

I have a patch branch here, but this repo currently blocks PR creation from non-collaborators:

https://github.com/openai/codex/compare/main...DJNgoma:codex:codex/bound-image-history-payloads?quick_pull=1

Commit:

383ac91 Bound image history payloads

The patch does two things:

  • Caps prepared prompt image data URLs at 10 MiB before they are persisted into conversation history, replacing oversized prepared images with the existing image-too-large placeholder.
  • Counts function-call output image URLs against the existing byte/token truncation budget instead of preserving them unbounded, so resumed legacy rollouts can omit oversized image items.

Validation run on the patch branch

just fmt
just test -p codex-utils-output-truncation
just test -p codex-core image_preparation
just fix -p codex-utils-output-truncation
just fix -p codex-core

The full just test suite was not run locally.

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    sessionIssues involving session (thread) management, resuming, forking, naming, archiving
    tool-callsIssues related to tool calling
    on Jun 28, 2026
  2. jroth1111 commented on Sep 11, 2026

    @jroth1111

    Additional evidence relevant to the proposed per-image cap: many individually sub-10-MiB images can still exceed the aggregate request limit.

    A deterministic test with real codex-cli 0.154.0 used an isolated temporary home, twelve distinct generated 1280×1280 PNGs in paired tool-call/output records, and a localhost Responses mock enforcing a 64 MiB JSON-body cap. Every encoded image was below 10 MiB, but the serialized request was 78,732,011 bytes and was rejected. A bounded recovery copy retaining the two newest images sent 13,175,417 bytes, included ten explicit references to archived originals, and completed successfully. The original rollout remained byte-for-byte unchanged. This is a client replay/transport test against a mock, not evidence of the production gateway's exact cap.

    There is also an important repair compatibility constraint: newer paginated rollouts have record ordinals and persisted database byte offsets. Shrinking earlier JSONL records can invalidate those offsets. The tested recovery copy preserves the original source, response items, tool-call/output IDs, image originals and metadata; it supplies a bounded replacement checkpoint under a new conversation identity. It refuses unsupported lineage/rollback cases. This is a local workaround, not a supported native repair API.

    Continued visual work subsequently regrew the affected active history to approximately 113 MiB. An offline watcher that waits for clients to close cannot prevent that active-session growth. A recovered copy was verified with a live provider turn, but sustained continuation and automatic goal-state transfer were not established.

    The detailed measurements, version limitations, pagination findings, and recovery tests are maintained in this report on #41338. Please include aggregate serialized-byte preflight for both inference and compaction, plus a supported recovery path, in the fix criteria. Per-image limits alone do not bound a multi-image conversation.

    Only synthetic measurements and sanitized aggregate observations are included. No private conversation IDs, paths, images, source documents, account data, credentials or raw logs are attached.

  3. BuhtigNotes13 commented on Oct 11, 2026

    @BuhtigNotes13

    A related image-limit/recovery observation, checked in recorded task results on October 11, 2026. This differs from the HTTP 413 in the original report; a shared cause is unproven.

    A long image-processing task on a connected desktop has a failed turn with HTTP 400, type invalid_request_error, param input, code image_request_too_large: “The combined resolved image content is too large. Reduce the number or size of input images.” The visible recovery instruction already requested one-source comparisons and resized previews, but that turn did not complete. We have not measured the actual submitted image payload or established how much history was included.

    A limited workflow workaround worked in this case: continue the work in a separate task using five-source allocations. That task's completed state was verified, and its output reports completed image batches. This did not repair the original task, and five images is not a measured or generally safe capacity limit; image sizes and accumulated history may differ. We have not verified whether the separate task inherited any earlier image history. App and OS versions were not recorded for this observation.

    The size rejection itself may be an expected input limit. The useful recovery question is whether the product can provide a supported way to reduce resolved image input in an existing task and continue it without editing internal history files. No community code was run or device settings changed for this report. No private images, paths, task IDs or raw transcripts are attached. Prepared with AI assistance.

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

    CLIIssues related to the Codex CLIbugSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archivingtool-callsIssues related to tool calling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions