Skip to content

Desktop threads can be poisoned by inline base64 tool images, leading to {"detail":"Bad Request"} on resume and likely inflated token usage #18629

Description

@mikezio

Summary

Codex desktop appears to persist image-producing tool output inline into replayable session history, especially as function_call_output items containing input_image payloads with data:image/...;base64,... content.

After enough of these accumulate, the thread can become unstable and start failing on later turns with:

{"detail":"Bad Request"}

This also appears likely to inflate model input usage for the affected thread because the replay payload becomes extremely large.

User-visible symptoms

  • A thread works normally for a while, then later starts failing with:
    {"detail":"Bad Request"}
  • The agent stops mid-session or fails on later continue attempts.
  • Once a thread reaches this state, it may keep failing repeatedly.
  • The affected thread/session becomes extremely large.

What I found locally

I inspected saved session JSONL data under ~/.codex/sessions/... and found many oversized function_call_output entries containing embedded inline image payloads such as:

  • type: "input_image"
  • image_url: "data:image/png;base64,..."

These image payloads are being persisted directly into replayable session history rather than stored out-of-line.

In the affected thread I observed:

  • repeated event_msg errors with:
    {"detail":"Bad Request"}
  • many very large function_call_output records containing base64 images
  • very large cumulative input-token counters in the same thread
  • app-state telemetry showing very large delta-byte growth around the same period

Likely root cause

The desktop app seems to serialize tool screenshots inline into session state.

A strong lead is in the bundled browser-use plugin code shipped with the app bundle. In the installed app I found code in both:

  • /Applications/Codex.app/Contents/Resources/plugins/openai-bundled/plugins/browser-use/scripts/browser-client.mjs
  • /Applications/Codex.app/Contents/Resources/plugins/openai-bundled/plugins/chrome/scripts/browser-client.mjs

that converts screenshot buffers into inline data URLs before emitting them:

processImage:t=>{
  if(!Buffer.isBuffer(t)) throw new Error(...);
  return `data:image/jpeg;base64,${t.toString("base64")}`
}

That means screenshot-heavy tool output can enter replayable thread state as huge inline strings.

Why this matters

This is more than a display bug:

  • it can permanently poison a thread
  • it can trigger replay/resume failures with 400 / {"detail":"Bad Request"}
  • it likely wastes context/token budget if these payloads are replayed or summarized into later requests
  • it probably affects any user doing longer sessions with image-heavy tools (Computer Use / browser-use / screenshot-producing tools)

Repro idea

  1. Start a long Codex desktop thread.
  2. Use Computer Use / browser-use or another screenshot-producing tool repeatedly.
  3. Continue the same thread many times.
  4. Inspect the saved session JSONL and observe large input_image / data:image/...;base64,... payloads inside function_call_output.
  5. Eventually the thread starts failing with:
    {"detail":"Bad Request"}

Expected behavior

Tool images should not be embedded as large base64 strings in replayable session history.

Instead, Codex should:

  • store image blobs out-of-line
  • persist only a file/blob reference or lightweight handle
  • cap / summarize oversized tool outputs before they enter replayable thread state
  • reject or sanitize replay-hostile payloads during persistence and/or resume

Actual behavior

Large inline base64 screenshots are persisted directly in function_call_output, and the thread later fails with:

{"detail":"Bad Request"}

Suggested fix

  • externalize tool images to blob/file storage before persistence
  • persist only lightweight references in session history
  • add hard caps/truncation for replayed tool output
  • prevent megabyte-scale inline payloads from being stored in replayable thread context
  • consider a repair/sanitization path for already-poisoned sessions

Notes

I also suspect this bug may contribute to inflated subscription/token usage for affected threads because the stored payload becomes massive and may be replayed to the model on later turns.

If helpful, I can provide a sanitized example session structure showing the oversized function_call_output / input_image pattern without exposing private data.

Activity

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions