Sending image causes unexpected cache invalidation
What issue are you seeing?
The first image sent within a session causes a significant or full prompt cache miss with OpenAI models, resulting in a one time spike in write cost.
While analyzing session data across providers and harnesses, I discovered a scenario of unexpected cache misses with OpenAI models. I noticed that midsession I would see significant cache misses. After digging into it, I found that the first time you send an image within a session, it results in a significant or full cache miss. This increases the write cost for the session. Sending more images in later turns does not seem to be affected by this bug.
I am able to reproduce this 100% of the time using codex, opencode, and pi. With codex, I tested different combinations of models and subscriptions. I tested with a plus and enterprise subscription along with a regular API key coupled with 5.6 luna/sol, 5.5, and 5.4.
Another interesting observation is that codex reported cache misses are usually not 100%, but tend to reflect a 50 to 75%+ miss. However, pi and opencode both report a 100% cache miss. That could be an issue within those harnesses, but I wanted to report the difference in case it helps with debugging.
Given the above, I assume this is a server side cache issue. That hypothesis is based on the API response information, so it's possible the bug is just in how the response is reported.
Also, just to call out, I have noticed a few other midsession unexpected cache misses (included in the example below). That could be a separate issue, but I have no way of reproducing it.
What steps can reproduce the bug?
I have attached three small scripts to test this across the harnesses listed above. Each reads the most recent session, so they should be easy to run. The turn where an image is sent is highlighted.
The easiest way to reproduce is to open a new session, send two messages, then send an image, then send another message. After that, look at the cached results using the scripts below. I say two messages because the first message is likely not fully cached. This should warm the session's cache usage above 90%, so the drop after sending the image is easy to notice.
Examples from all three scripts are included below. In the simple Codex example, I also sent two additional messages about an hour later. The first of those produced another cache miss, which may be useful for debugging.
<details>
<summary>Simple codex session</summary>
Session: /Users/danclark/.codex/sessions/2026/07/12/rollout-2026-07-12T11-26-05-019f56ef-8430-7843-a950-9c75ef6bfc19.jsonl
Model: gpt-5.5
T.C ACTION INPUT CACHE MISS HIT D_INPUT D_CACHE LOST OUT RSN FLAG
1.1 final response 14664 5504 9160 37.5% - - - 72 0 -
2.1 final response 14744 14208 536 96.4% +80 +8704 - 34 0 -
3.1 image + final r~ 14881 1920 12961 12.9% +137 -12288 12288 108 40 CACHE_REGRESSION
4.1 final response 14954 14720 234 98.4% +73 +12800 - 8 0 -
For more insights, I sent two more messages, 1 hour later, and this is what the script output:
5.1 final response 14969 1920 13049 12.8% +15 -12800 12800 7 0 CACHE_REGRESSION
6.1 final response 14983 14720 263 98.2% +14 +12800 - 7 0 -
</details>
<details>
<summary>Longer codex session</summary>
Session: /Users/danclark/.codex/sessions/2026/07/12/rollout-2026-07-12T11-01-17-019f56d8-cf42-7ff1-a647-db75c1a3030c.jsonl
Model: gpt-5.6-luna
T.C ACTION INPUT CACHE MISS HIT D_INPUT D_CACHE LOST OUT RSN FLAG
1.1 tool: exec -> e~ 14403 8960 5443 62.2% - - - 385 213 -
1.2 tool: exec -> e~ 15026 14080 946 93.7% +623 +5120 - 354 82 -
1.3 tool: exec -> e~ 26411 14080 12331 53.3% +11385 +0 - 324 21 -
1.4 tool: exec -> e~ 36614 25344 11270 69.2% +10203 +11264 - 148 34 -
1.5 tool: exec -> e~ 37724 35584 2140 94.3% +1110 +10240 - 336 12 -
1.6 tool: exec -> e~ 48447 8960 39487 18.5% +10723 -26624 26624 528 101 CACHE_REGRESSION
1.7 tool: exec -> e~ 60930 47872 13058 78.6% +12483 +38912 - 377 43 -
1.8 tool: exec -> e~ 71992 60160 11832 83.6% +11062 +12288 - 512 150 -
1.9 tool: exec -> e~ 83836 71424 12412 85.2% +11844 +11264 - 125 29 -
1.10 final response 88230 83712 4518 94.9% +4394 +12288 - 11941 10876 -
2.1 final response 100341 87808 12533 87.5% +12111 +4096 - 3697 3104 -
3.1 tool: exec -> u~ 104148 100096 4052 96.1% +3807 +12288 - 1561 1368 -
3.2 tool: exec -> e~ 105733 103168 2565 97.6% +1585 +3072 - 3640 3542 -
3.3 tool: exec -> a~ 110053 105216 4837 95.6% +4320 +2048 - 1059 31 -
3.4 tool: exec -> e~ 111136 109312 1824 98.4% +1083 +4096 - 253 129 -
3.5 model 111136 109312 1824 98.4% +0 +0 - 253 129 -
4.1 tool: exec -> e~ 113521 111360 2161 98.1% +2385 +2048 - 394 144 -
4.2 tool: exec -> a~ 119729 112384 7345 93.9% +6208 +1024 - 1488 654 -
4.3 tool: exec -> e~ 121241 119552 1689 98.6% +1512 +7168 - 113 15 -
4.4 tool: exec -> e~ 125748 120576 5172 95.9% +4507 +1024 - 690 585 -
4.5 tool: exec -> a~ 127255 124672 2583 98.0% +1507 +4096 - 484 237 -
4.6 tool: exec -> u~ 127763 126720 1043 99.2% +508 +2048 - 172 36 -
4.7 tool: exec -> e~ 127959 126720 1239 99.0% +196 +0 - 149 21 -
4.8 tool: exec -> a~ 128391 127744 647 99.5% +432 +1024 - 248 69 -
4.9 tool: exec -> e~ 128663 127744 919 99.3% +272 +0 - 70 0 -
4.10 tool: exec -> e~ 128774 127744 1030 99.2% +111 +0 - 520 444 -
4.11 tool: exec -> a~ 131683 127744 3939 97.0% +2909 +0 - 373 258 -
4.12 tool: exec -> e~ 132080 130816 1264 99.0% +397 +3072 - 123 51 -
4.13 tool: exec -> u~ 132244 131840 404 99.7% +164 +1024 - 122 9 -
4.14 final response 132390 131840 550 99.6% +146 +0 - 862 516 -
5.1 image + tool: e~ 133712 8960 124752 6.7% +1322 -122880 122880 5592 4780 CACHE_REGRESSION
5.2 tool: exec -> e~ 139328 132864 6464 95.4% +5616 +123904 - 157 38 -
5.3 tool: exec -> a~ 141510 139008 2502 98.2% +2182 +6144 - 152 57 -
5.4 tool: exec -> e~ 141686 141056 630 99.6% +176 +2048 - 343 271 -
5.5 final response 142070 141056 1014 99.3% +384 +0 - 771 590 -
^ the first cache regression is the small callout I made in the first section of the issue ticket.
</details>
<details>
<summary>Simple opencode session</summary>
T.C MODEL IMG NEW READ WRITE CACHED RETAIN PREV LOST OUT RSN REPORTED CACHE
1.1 gpt-5.6-sol - 7645 0 0 0.0% - - - 111 0 $ 0.000000 baseline
1.2 gpt-5.6-sol - 1462 6656 0 82.0% 87.1% 7645 989 76 0 $ 0.000000 partial-hit
1.3 gpt-5.6-sol - 1004 7680 0 88.4% 94.6% 8118 438 120 22 $ 0.000000 hit
1.4 gpt-5.6-sol - 13445 0 0 0.0% 0.0% 8684 8684 298 0 $ 0.000000 full-miss
2.1 gpt-5.6-sol - 6076 7680 0 55.8% 57.1% 13445 5765 18 0 $ 0.000000 partial-hit
3.1 gpt-5.6-sol - 993 12800 0 92.8% 93.1% 13756 956 5 0 $ 0.000000 hit
4.1 gpt-5.6-sol Y 13868 0 0 0.0% 0.0% 13793 13793 5 0 $ 0.000000 full-miss
5.1 gpt-5.6-sol - 1081 12800 0 92.2% 92.3% 13868 1068 8 0 $ 0.000000 hit
</details>
<details>
<summary>Simple pi session</summary>
T.C PROVIDER MODEL IMG INPUT CACHE WRITE NEW OUT RSN REPORTED CACHE
1.1 openai-codex gpt-5.6-sol - 2863 0 0 2863 30 0 $0.015215 baseline
2.1 openai-codex gpt-5.6-sol - 341 2560 0 341 163 0 $0.007875 hit
3.1 openai-codex gpt-5.6-sol - 512 2560 0 512 8 0 $0.004080 hit
4.1 openai-codex gpt-5.6-sol Y 583 2560 0 583 85 10 $0.006745 hit
4.2 openai-codex gpt-5.6-sol - 3627 0 0 3627 39 0 $0.019305 full-miss
5.1 openai-codex gpt-5.6-sol - 1115 2560 0 1115 7 0 $0.007065 hit
</details>
What is the expected behavior?
I would not expect that sending an image would invalidate all or most of the cache. I would expect the image to be added as new session content, with the existing session cache being retained.
I have tested sending images with grok 4.5 in opencode and Opus in pi and did not see this behavior.
Additional information
Here are the scripts I have used for analysis. Requires jq and sqlite3 (for opencode)
opencode-session-debug.sh
codex-session-debug.sh
pi-session-debug.sh