[Bug] iOS Simulator screenshots trigger persistent messages_changed cache misses and full 1h message-history rewrites
Preflight checklist
- [x] I searched existing reports, including #63930. This report has the same cache-read collapse but a different, narrower trigger: repeated iOS Simulator screenshot results in Claude Desktop.
- [x] I am not attaching the transcript, screenshots, project path, account identifiers, or credentials.
Summary
In a long Claude Desktop session using Claude Opus 5, iOS Simulator screenshot tool results entered a persistent message-history cache-miss state. After the transition, nearly every screenshot caused the message cache to be rebuilt at 1-hour cache-write pricing instead of extending/reusing the existing message-history cache.
The response diagnostics explicitly report cache_miss_reason: "messages_changed". The system/tools prefix remains cached, but the message-history cache collapses and is rewritten repeatedly.
Environment
- Claude Desktop / Claude Code bundle:
2.1.229 - Platform: macOS
- Model:
claude-opus-5 - Tool returning images:
mcp__Claude_Code_iOS_Simulator__controlwithaction: "screenshot" - Screenshot scale values observed:
0.3,0.35,0.4, and0.5 - Session used 1-hour prompt-cache writes (
cache_creation.ephemeral_1h_input_tokens)
Healthy behavior in the same session
Before the regression, many screenshots reused a growing message cache normally. For example, the request immediately following each screenshot from 18:52 through 18:57 EDT had:
cache_read_input_tokens: ~685k to ~716kcache_creation_input_tokens: ~2,540
This is the expected pattern: the large existing prefix is read from cache and only the new screenshot/tool-result delta is written.
Regression boundary
The transition is abrupt inside the same session:
~~~text
18:57:47 EDT cache_read=716,112 cache_creation=2,540 (healthy)
18:57:56 EDT iOS Simulator screenshot result returned
18:58:28 EDT cache_read=36,832 cache_creation=627,302 cache_miss_reason=messages_changed
~~~
After this point, cache reads repeatedly fall to ~36.8k (consistent with a surviving system/tools prefix) while ~0.63M-0.76M message-history tokens are re-created. The screenshot payload size does not explain the transition: earlier screenshots of comparable or greater base64 size, including the same scale values, had healthy cache reuse.
Later reproduction in the same session
After an idle period, the issue recurred in two short screenshot bursts. I deduplicated repeated local assistant-log entries by message.id before counting usage (the Desktop transcript can persist an identical model response 2-3 times within milliseconds).
- Time window: 21:40-22:40 EDT
- Human messages: 3
- Unique model responses: 47
- Tool-loop responses (
stop_reason: "tool_use"): 44 - iOS Simulator screenshot results: 15
- Large cache writes: 14
- Initial large write:
previous_message_not_foundafter an idle period - Remaining large writes with
messages_changed: 13 - Each
messages_changedwrite: ~744k-764kcache_creation_input_tokens - Each
messages_changeddiagnostic reported ~575k-590k missed message tokens
Thirteen of the fourteen large cache writes occurred 19-32 seconds after an iOS Simulator screenshot result. In that hour, the affected session recorded 10,564,098 one-hour cache-write tokens and 24,993,942 cache-read tokens. The public API list-price equivalent of the 1-hour cache writes alone is about $105.64; this is not claimed as the actual charge for a subscription user.
Expected behavior
Repeated screenshots in a stable long-running session should preserve the existing message-history cache. The next request should read the long cached prefix and write only the genuinely new tool-result/message delta.
Actual behavior
Once the session enters the messages_changed state, each screenshot can cause the message cache to be abandoned and hundreds of thousands of tokens of history to be re-written. The session remains in this high-cost state across later screenshot bursts.
Relation to #63930
This looks closely related to #63930: cache_read collapses to a small system/tools floor while cache_creation becomes nearly the entire message history. In this trace the reproducible correlated trigger is an iOS Simulator screenshot result, not a turn with many parallel tool calls. The local cache_miss_reason diagnostics make the history-level invalidation directly visible.
I can provide additional sanitized per-request usage rows privately if helpful.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗