Sending image causes unexpected cache invalidation

Open 💬 0 comments Opened Jul 12, 2026 by dpclark4

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

View original on GitHub ↗