Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Related but different: #15420 (generated images missing from replies; closed as covered by #19066, which refs #19059), #18258 (closed unmerged; alternative fix for the missing-image bug, dedupes by path only), #29957 (open; touches the same function but is about unclosed HTML blocks, unrelated).
Area
apps/server (with packages/shared)
Steps to reproduce
- In a Codex thread, ask for an image edit that Codex saves and then copies into the project, e.g.
cp ~/.codex/generated_images/<session>/<id>.png ./game-03.png. - Codex ends its reply with
(the copy). - Separately: in one turn, let Codex call image generation several times (a first draft, then corrections), and embed only the one it chose.
Expected behavior
Each image is shown once. When the reply already embeds an image, T3 doesn't append drafts the agent discarded.
Actual behavior
T3 appends  under the reply for every image generated in the turn whose exact path the reply doesn't embed:
- Case 1: the copied file and the appended original are byte-identical (same MD5), so the same picture shows twice. This happened on every turn of the thread.
- Case 2: one turn generated 4 images and the reply embedded a copy of one. The chat showed 5 images: the copy, its original again, and 3 other generations the agent did not choose (one shows the defect it was fixing).
Cause
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts (projectGeneratedImages, ~L6534) passes every savedPath for the turn (state.saved.values()) to appendGeneratedImageEmbeds.
packages/shared/src/generatedImageMarkdown.ts suppresses an image only if the reply embeds that same normalized path. A copy at another path, or a superseded draft, isn't recognized, so all are appended. This came in with #19066 (refs #19059; #15420, images missing when the reply embeds none, was closed as covered by it). generatedImageMarkdown.test.ts covers path-spelling equivalence only, with no case for a copy or a superseded draft.
Possible fix
- If the final reply already embeds any local image, append nothing (the agent chose what to show). Fall back to appending only when the reply embeds no image, and then perhaps only the latest generation. Caveat: a reply that embeds an unrelated local image (e.g. a screenshot) would then hide a real generation, so this may need to match only images under the generated-images dir or copies of them.
- Or dedupe by file content (size + hash) instead of path, which fixes case 1 but not case 2, and means the server reads the embedded local files.
Impact
Minor bug or occasional failure: confusing duplicate images, and rejected drafts shown as if they were results.
Version or commit
0.1.10-nightly.20261113.4630; code read at main @ 4aec277
Environment
macOS 27.2, T3 Code desktop (Nightly), Codex provider, native image generation
Live repro (same version)
Needs Codex with ChatGPT sign-in (the native image tool isn't offered with an API key). New thread, one prompt:
Please do exactly these steps, in this one turn:
1. Use your built-in image generation tool to generate a simple picture: a red circle on a white background.
2. Use your image generation tool a second time to generate a blue square on a white background.
3. Copy ONLY the red circle image into this folder as `circle.png` with `cp`.
4. End your reply with one short sentence and exactly one Markdown image embed of the copy: `` using the absolute path of circle.png. Do not embed or link the blue square or the original generated files.
Codex's final answer embedded one image:
Copied the red circle to `circle.png`.
What T3 stored and rendered:
Copied the red circle to `circle.png`.


circle.pngandexec-6aed6a0d….pnghave the same MD5, so the red circle shows twice.exec-0d5730ab….pngis the blue square, which the agent was told not to show and didn't.
repro-duplicate-images.mov
Investigated by Claude Opus 5.5 and reviewed by Fable 5.2.
Activity
juliusmarminge commentedon Nov 13, 2026
I tried to reproduce this on the current build and could not.
Commit 4aec277 (origin/main). Live run blocked: Codex on the VM authenticates with an API key, and
codex exec -m gpt-6-lunaasked to generate an image replied that no image generation tool is available in this session (Codex offers its native image tool only with ChatGPT sign-in). Instead I drove the real CodexAdapterV2 through its replay harness (real Codex app-server frames: two imageGeneration item/completed events with savedPath under ~/.codex/generated_images, then a final answerUpdated the sprite.\n\n). On main the final assistant message becomes the reply plusand, i.e. the original of the embedded copy is shown again and the unchosen draft is appended. Test row added to the existing 'projects generated images' table in apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts; it fails on main, the other rows pass.Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.
dotrahat commentedon Nov 13, 2026
@juliusmarminge This still happens. I added a live repro on 0.1.10-nightly.20261113.4630 to the description: the exact prompt, the reply before and after T3 added images, and a recording. Codex needs a ChatGPT sign-in for its image tool. Your replay test failing on main shows the same behaviour. Could this be reopened?