
Codex Prompt Cache Keys for Parallel Subagents
Codex’s new PromptCacheKey policy lets hosts keep session identity separate from prompt-cache routing. Learn how to choose a shared key for parallel subagents, omit the field for compatible gateways, and reapply the policy when resuming threads—plus caveats for cache hits and prompt consistency. Also: updates to fork-prefix preservation, authentication errors, Guardian metrics, and compaction recovery.
Chapter 1
What changed and why fan out hurts
Ethan Park
Okay, we're rolling. Thanks to Jellypod for helping make this daily show a reality. Today is one change that matters if you run Codex with parallel subagents and you look at your token bill. Pull request 55587, merged October 20th, authored by rm-openai. The title is "Allow callers to control prompt cache keys independently of session IDs."
Maya
Here's the mechanism. Codex sends a prompt_cache_key with model requests, and historically that key was derived from the session ID. One session, one key. That's fine for a single conversation. Multi agent V2 breaks it, because every subagent gets its own session manager and its own session ID. So a worker that shares thousands of tokens of repo context, system instructions, and tool definitions with ten siblings still sends a different cache key. There's an open issue in the pi repo, number 6468, describing exactly this pattern in coding agent SDKs: fan out sends a different prompt cache key for each subagent.
Ethan Park
And the cost side comes from OpenAI's prompt caching docs. For GPT 5.6 and later, cache writes cost 1.25 times the standard uncached input rate on the default thirty minute retention. Cache reads are heavily discounted, in the range of 90 to 95 percent off. So if your key differs per worker, each worker can pay the write premium on a prefix that was already identical. Same bytes, paid for repeatedly. That's the interpretation of the issue plus the docs, not a measured number from this pull request, so check your own usage before promising anyone savings.
Maya
So what changed is that StartThreadOptions now exposes PromptCacheKey with three policies. Default keeps the old behavior, the session derived key. Custom lets you pass your own string. Omit strips prompt_cache_key from outbound model requests entirely.
Ethan Park
The important design choice is the separation. Per the pull request, session identity headers stay independent of this policy. So your tracing IDs, connection identity, and thread persistence don't change when you set a custom key. You're only touching the field the model API uses for cache routing. And the policy propagates to child agents, delegated sessions, and Guardian review sessions. You set it once on the parent thread and spawned subagents inherit it.
Chapter 2
How to use it and what to watch
Maya
Let's make it concrete. Say you run an orchestrator that fans out eight workers over the same repository. Each gets the same instructions and tools, then a different slice of the task. You'd start the parent thread with PromptCacheKey Custom, and pick a key built from stable things: something like workflow, then your project name, then an instruction version. For example, workflow colon myrepo colon v1. Bump the version when you change the shared instructions. Then the first worker pays the write, and later workers can read the warmed prefix instead of writing again.
Ethan Park
Keep the key low cardinality. OpenAI's caching docs warn that on older models, sending more than roughly fifteen requests per minute under a single key can overflow a machine's capacity. At the other extreme, a key that's unique per request destroys your hit rate. So you want a bounded key per shared prefix, not per worker and not one global key for everything. If you run several unrelated pipelines, give each its own key.
Maya
Second use case is Omit. If you point Codex at a custom Responses compatible gateway, think a proxy in front of a local model, some of those can misroute or reject requests with a prompt_cache_key they don't expect. Omit lets you drop the field without touching how sessions work. Everything else stays the same. The exact JSON spelling over app server RPC, check the generated schema in your build, since I'm not going to guess field names on air.
Ethan Park
Now the caveats, and there are three. First, the one the commit itself calls out: hosts must reapply the policy when resuming a thread from storage. If you resume a saved thread, don't assume your custom key came back with it. Your harness has to pass the policy again, otherwise you're likely back on the default and your fan out quietly returns to per session keys. Worth adding a test that checks the outbound request on resume.
Maya
Second, the key only helps with routing. It doesn't make mismatched prompts match. If tool schemas differ, reasoning effort differs, or the ordering of developer messages diverges before the cache breakpoint, you'll still miss, whatever key you send. So keep the shared prefix byte for byte identical, and put the per worker task text at the end. Third, this is on the 0.164 line, which is currently in alpha releases. The pull request merged to main on October 20th, so confirm it's in the build you run before relying on it.
Ethan Park
A few smaller things from the last day or two. Multi agent V2 now defaults features.multi_agent_v2.preserve_fork_prefix to true. Full history forks keep the parent's model visible history and append the child instructions after it, including for grandchild forks. If you relied on instruction filtering, set it to false explicitly. That default also helps the cache story, since a preserved prefix is what makes forks cache friendly.
Maya
Also, auth header construction is now fallible. Before, if managed authentication became unavailable, building headers could silently return no credentials. Now it returns an error instead of sending a request with missing or partial auth. Transient MCP auth errors get retried, while build errors and policy denials are terminal. There's a new metric, codex.guardian.turn.blocking_ms, that measures how long Guardian approvals block a turn, counting overlapping waits once. And a fix for a panic after background compaction fails before a checkpoint. It now falls back to foreground compaction.
Ethan Park
So the action item: if you run parallel subagents, look at your request logs and see whether sibling workers send different cache keys over the same prefix. If they do, a shared custom key is the first thing to test, and then compare cache read and write token counts before and after. That's what tells you if it worked.