protonscr

NVIDIA shader cache: shadercachemanager merge logs "Mismatching key!" and shrinks steamapp_merged_shader_cache.bin (cold cache after every exit, follow-up to #11392)

steamopen NVIDIA drivers
ValveSoftware/steam-for-linux#13622 · opened 2026-09-15 by yotamleo · updated 2026-10-28 · 16 comments · github
Yyotamleo 2026-09-15 github

On NVIDIA, a game that pre-cached its shaders through Steam stutters as if the cache were cold on the next launch after an exit or a new Shader Swarm bucket, because Steam's shadercachemanager loses entries when it merges NVIDIA cache fragments into steamapp_merged_shader_cache.bin. With large pipeline sets (Overwatch 2) that means minutes of 100% CPU DXVK compiling and low fps on most launches, and the ~15 minute "Processing Vulkan shaders" run after each update is largely wasted.

This looks like the root cause of the shrink reported in #11392 (7.5 GB -> 4.5 GB after a game session with __GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1 set), which was attributed to driver pruning there. The size-limit part of #11392 is real and is fixed by __GL_SHADER_DISK_CACHE_SIZE; the shrink below is logged by Steam's own merge step, not by the driver.

Your system information

  • Steam client version: 1788652215
  • Distribution: CachyOS, kernel 7.2.5
  • Opted into Steam client beta?: No
  • Have you checked for system updates?: Yes
  • GPU / driver: RTX 4090, nvidia-open 610.57.04 and 615.71.09 (reproduces on both)
  • Game: Overwatch 2 (2357570), Proton
  • Shader pre-caching and background processing: enabled
  • Filesystem: btrfs, 3.4 TB free

Please describe your issue in as much detail as possible:

Every time shadercachemanager merges NVIDIA fragments into
steamapps/shadercache/<appid>/nvidiav1/GLCache/<hash>/<hash>/steamapp_merged_shader_cache.bin,
logs/shader_log.txt prints thousands of Mismatching key! Entry N/M. lines,
and the merged file keeps less than it was given or gets smaller:

When Merge trigger Mismatching key! lines Merged .bin
driver 610.57 merge + crawl (a host hang earlier that day may have contributed) 18,240 (+ FOZ underflow, file truncated?) 4.3 GB, not used on relaunch
driver 610.57 game exit, folding in the game's steamapp_shader_cache1 fragment 40,558 6.94 GB -> 4.80 GB
driver 615.71.09 new bucket after a game update 29,582 4.40 GB
driver 615.71.09 after fossilize_replay finished: 31 fragments totalling 7.4 GB 3,469 4.40 GB -> 5.21 GB, fragments deleted

On the 610.57 run the first launch after pre-caching did read the merged file
(4.5 GB read in ~2 minutes, warm). One exit later the merge above ran, and the
second launch was cold again (3 DXVK compiler threads at 100%, +149 MB of new
cache in 70 s).

Log shape:

[..23:37:27] Mismatching key! Entry 1053285538/13703.
... 29,580 more lines ...
[..23:37:38] Mismatching key! Entry 111463556/33079.
[..23:37:38] Done with merging potential NVIDIA fragments.

Timing detail that matters for workarounds: the fragments are merged and deleted a few seconds after the fossilize_replay process exits, before fossilize_replay completed, remove from build queue. is logged.

Steps for reproducing this issue:

  1. NVIDIA GPU, shader pre-caching on, a game with a large pipeline set (Overwatch 2).
  2. Let "Processing Vulkan shaders" finish. Launch, play a match, exit.
  3. grep -c 'Mismatching key' ~/.local/share/Steam/logs/shader_log.txt and compare the size of steamapp_merged_shader_cache.bin before and after the exit.
  4. Launch again: DXVK compiles as on a cold cache.

Workaround (works on 615.71.09)

  1. Keep the driver's writable cache away from the merger with launch options:
    __GL_SHADER_DISK_CACHE_PATH=$HOME/.cache/nvidia-<game> __GL_SHADER_DISK_CACHE_APP_NAME=<game> __GL_SHADER_DISK_CACHE_SIZE=17179869184 __GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1
  2. When fossilize_replay exits, copy the fresh steamapp_shader_cache*.{bin,toc} fragments (and the merged file) into the same GLCache/<hash>/<hash>/ dir under that path, before Steam merges and deletes them. A small watcher polling pgrep -x fossilize_repla is enough.
  3. List them as read-only caches. The separator is a semicolon, and every name must be exact; a comma-separated list or the bare steamapp_shader_cache prefix is silently ignored by the driver:
    __GL_SHADER_DISK_CACHE_READ_ONLY_APP_NAME="steamapp_merged_shader_cache;steamapp_shader_cache0;steamapp_shader_cache1;...;steamapp_shader_cache30"

With that, the game holds all 32 read-only caches open, its own cache grows by 0 bytes over the first minutes, and warm launches stay warm across exits.

Related: #11392 (driver size-limit pruning, and the shrink above), #9803 (precompiling into cache dirs the driver never reads).

Ggeromet 2026-09-16 github

ChatGPT 5.6 Sol report, because I am just a frustrated human who wanted to play a game hours ago.

I appear to be hitting the same failure pattern with No Man's Sky (AppID 275850) on NVIDIA.

System:

  • Fedora 44 KDE / Wayland
  • NVIDIA GeForce RTX 4060 Laptop GPU
  • NVIDIA driver 595.80
  • Hybrid graphics laptop (AMD 680M + NVIDIA), with NMS's Steam shader cache clearly being generated under the NVIDIA nvidiav1/GLCache path

I originally mentioned the repeated shader processing behavior in ValveSoftware/Proton#7176, but after finding this issue I checked shader_log.txt and the symptoms here look much more relevant.

Current count:

$ grep -c 'Mismatching key' ~/.local/share/Steam/logs/shader_log.txt
39933

So my current shader_log.txt contains 39,933 Mismatching key entries.

I have not yet isolated that count to NMS alone, so I don't want to claim all 39,933 lines belong to AppID 275850. However, the log around NMS's cache processing also contains repeated:

[2026-09-16 22:34:27] FOZ underflow, file truncated?
[2026-09-16 22:34:27] FOZ underflow, file truncated?
...
[2026-09-16 22:34:28] FOZ underflow, file truncated?

Immediately afterwards Steam processes the NMS NVIDIA cache:

[2026-09-16 22:34:28] Finding NVIDIA cache files, base directory: /home/anon/.local/share/Steam/steamapps/shadercache/275850.

It then walks a large set of fragments:

steamapp_shader_cache0.toc
steamapp_shader_cache1.toc
steamapp_shader_cache2.toc
...
steamapp_shader_cache14.toc
steamapp_merged_shader_cache.toc

followed by:

[2026-09-16 22:34:31] Writing NVIDIA cache of 1 buckets into /home/anon/.local/share/Steam/steamapps/shadercache/275850.
[2026-09-16 22:34:34] Processing NVIDIA cache file at .../steamapp_merged_shader_cache.toc.
[2026-09-16 22:34:36] Done with merging potential NVIDIA fragments.

Then, only about 24 minutes later, Steam walks the same NMS fragment set again:

[2026-09-16 22:58:04] Finding NVIDIA cache files, base directory: /home/anon/.local/share/Steam/steamapps/shadercache/275850.
...
[2026-09-16 22:58:13] Processing NVIDIA cache file at .../steamapp_merged_shader_cache.toc.
[2026-09-16 22:58:15] Writing NVIDIA cache of 1 buckets into /home/anon/.local/share/Steam/steamapps/shadercache/275850.
[2026-09-16 22:58:30] Done with merging potential NVIDIA fragments.

This is accompanied by the user-visible symptom that prompted me to investigate: No Man's Sky repeatedly downloads/processes a very large Vulkan shader cache instead of remaining warm between Steam/game launches. I recently had Steam download/process roughly 7.8 GB for NMS despite there not having been a corresponding NMS update at that time.

That seems consistent with the behavior described in this issue: a large NVIDIA cache is generated, Steam attempts to merge the fragments, there are huge numbers of key mismatches / FOZ read errors, and useful cache contents apparently do not survive in a way that prevents subsequent processing.

One possibly interesting difference from the original report is that this reproduces for me on NVIDIA 595.80, while the original report lists 610.57.04 and 615.71.09. So if it is the same shadercachemanager bug, it does not appear to be specific to the newer 610/615 driver branches.

I have not done the controlled before/after steamapp_merged_shader_cache.bin size measurement yet because I don't want to restart NMS again right now, so I can't independently confirm the cache shrink portion yet. I can collect that comparison on a later launch if useful.

The combination I can confirm right now is:

  • No Man's Sky / AppID 275850
  • NVIDIA shader cache
  • repeated huge Vulkan shader processing/downloads
  • 15 steamapp_shader_cache* fragments being merged
  • repeated merge passes
  • FOZ underflow, file truncated?
  • 39,933 Mismatching key entries currently present in shader_log.txt

This looks like a strong second instance of the same failure signature on different hardware, distro, game, and NVIDIA driver version.

Zzzallirog 2026-09-25 github

Independent reproduction with controlled before/after measurements, on a different GPU, game and distro. It adds one result I haven't seen reported yet: on NVIDIA, the pipelines Steam's own replay compiles are not used by the game at all.

System: Arch Linux, RTX 4060 Laptop, nvidia-open 615.71.09, Steam client 1788652215, THE FINALS (2073850, UE5/DX12 → vkd3d-proton) on GE-Proton10-34. __GL_SHADER_DISK_CACHE_SIZE=48000000000 and __GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1 are set system-wide, so driver pruning is ruled out. The metric is bytes the game writes to nvidiav1 during its own PSO preload (PRELOADING SHADERS in the main menu). Cold cache is 413 MB.

1. Steam's replay output is not reused by the game. I moved nvidiav1 and the replay_cache* / steam_pipeline_cache_whitelist* files aside and let Steam run a full background replay:

  • Args, from /proc: --num-threads 4 --spirv-val --on-disk-validation-whitelist … --replayer-cache … --device-index 0 --timeout-seconds 10.
  • Env: __GL_SHADER_DISK_CACHE_APP_NAME=steamapp_shader_cache, READ_ONLY_APP_NAME=steam_shader_cache;steamapp_merged_shader_cache.
  • It ran 18:44 → 19:45 and reached 94% of 381k pipelines (Done with replay), producing a 3.76 GB merged file.

Only the compiled cache and Fossilize's bookkeeping were cleared, not the recipes. The replay inputs included steamapp_pipeline_cache.foz, the pipelines recorded from this game's own sessions, as well as the swarm steam_pipeline_cache.foz.

On the next launch the game's preload wrote 410 MB, the same as with no cache at all. An hour of replay saved the game nothing, even for pipelines recorded from the game itself. My guess, unverified, is that the replayed pipelines don't produce the same driver cache keys as the game's own pipeline creation.

2. Every merge I observed, measured.

Merge Base merged file Input Mismatching key! Result
09-24 15:18:56 long-lived (dir from 09-12, driver-update day) game relaunched 3 s earlier, still starting 25,800 ?
09-24 15:19:40 long-lived second batch, game running 15,039 ?
09-24 20:00 long-lived after game exit 15,250 4.30 GB
09-25 17:46 long-lived replay shards 1.24 GB + 10 s game run 13,203 4.30 → 4.61 GB
09-25 18:43 long-lived after a ~37 min match 13,227 4.61 → 4.30 GB (−311 MB after a full match)
09-25 19:45 none (fresh) pure replay, 4 worker fragments 4.67 GB 0 3.76 GB
09-25 20:26 fresh (from 19:45) after a match, 435 MB game fragment 0 3.76 → 4.197 GB, lossless
09-25 20:55 copy of the 18:43 result after a short session, small fragment 61 +0.3 MB

The fresh merged file stayed at zero mismatches, even after a match. The thousands only showed up on the long-lived file. The 20:55 run, which was small, got 61 even on that file. I can't tell from outside what drives the count, whether it's the fragment size, entries left over from the previous driver, or something else.

3. Shrink vs. usable cache. This part I could not reproduce. A copy of the cache right after the lossy 18:43 merge (−311 MB, 13,227 mismatches) was still warm on relaunch: the preload wrote 192 bytes. The same held after the 17:46 merge. So here the loss didn't hit what the game needs at load, unlike the Overwatch 2 report above. It may still hit pipelines first used mid-match, which a menu preload doesn't exercise. Possibly DX11/DXVK vs DX12/vkd3d matters.

4. A merge that overlapped a game start. On 09-24 the game exited at 15:18:46 and was relaunched at 15:18:53 (gameprocess_log). The merge of NVIDIA fragments began at 15:18:56, while the new game process was still spawning, and logged 25,800 Mismatching key! lines, then 15,039 more at 15:19:40. At 15:20:32 the upload job skipped entries in steamapp_merged_shader_cache.bin whose offsets (up to ~872 MB) exceed the shaderBuffer it had read (341 MB), so the index and the data it saw didn't agree at that point. My guess, unverified: a merge that overlaps a running game can leave the long-lived file inconsistent, and the steady 13–15k mismatches on its later merges are what's left of that. No merge of the fresh file overlapped a game start, and it stayed at 0. Steam keeps only two shader logs, so I can't check how often this overlap happened before 09-24.

Workaround that holds here. Pre-caching and background processing off, plus a launch-option prefix. The prefix points __GL_SHADER_DISK_CACHE_PATH at a directory Steam never merges, with the same APP_NAME / READ_ONLY_APP_NAME as above. The game keeps its own cache, and Steam's merge never touches it.

Since both the replay output that goes unused and the merge happen inside shadercachemanager (clientdll/shadercachemanager/shadercachemanager.cpp, CShaderCacheImplNVIDIA), this looks like a Steam client bug rather than a driver one. Could the label be reconsidered?

Zzelect0r 2026-09-26 github

Your system information

Steam Version: 1788652215
Distribution: CachyOS, 7.2.7-1-cachyos
Steam client beta: No
Have you checked for system updates: Yes
GPU / driver: RTX 2070, nvidia-open 615.71.09
Game: cs2, Proton-CachyOS-11-20260703-slr-x86_64_v3
Shader pre-caching and background processing: enabled
Filesystem: btrfs
Filesystem SteamLibary: ext4

I noticed that after switching to Proton-CachyOS-11-20260703-x86_64_v3,
i encountered issues with extremely long and recurring shader compilation every time i restarted steam or the game.
After switching back to the older version I was using previously,
behavior seems to have returned to normal.
I also noticed that the older version maxes out CPU usage constantly and allocates more RAM.
Consequently, i am continuing to use Proton-CachyOS-11-20260602-slr-x86_64_v3!

Change the version within the package "protonup-qt" and set it up in steam settings as default compatibility tool.

Edit: It loads 6gb again ... :/

Eedmundselliot 2026-10-05 github

I reproduced this with native CS2 on Ubuntu 26.04 and NVIDIA 610.57.04, and found byte-level evidence strongly pointing to a uint32 offset wraparound at exactly 2**32 bytes (4 GiB) in the merged NVIDIA cache.

The preserved steamapp_merged_shader_cache.bin is 4,377,597,661 bytes. Its .toc contains 126,127 entries:

  • The first 120,021 keys match their indexed locations.
  • The remaining 6,106 fail at those locations, but every one matches exactly 2**32 bytes farther into the binary.
  • Adding 2**32 to those wrapped offsets makes all record boundaries contiguous, including across the overflow boundary.

The boundary is explicit:

Entry 120020:
  Stored offset:    4,294,956,110
  Payload size:            17,699
  Record overhead:             32
  Next physical offset: 4,294,973,841

Entry 120021:
  Stored offset:            6,545
  Physical offset:  4,294,973,841
  Difference:       4,294,967,296 = 2**32

4,294,973,841 % 2**32 == 6,545

Steam reread this file after the game-exit merge and logged exactly 6,106 Mismatching key! warnings. Every logged offset/size pair corresponds to one of these wrapped entries, accounting for the logger's record-header adjustment (stored offset + 20, payload size + 12). Steam also reported finding 120,021 NVIDIA shaders, matching the number of entries before the wrap.

This is much more specific than generic cache corruption: the data is present, but the index points exactly 4 GiB too early. I cannot inspect the proprietary merger implementation, so I cannot identify the precise integer conversion or assign the internal implementation fault definitively, but the saved artifact demonstrates modulo-2**32 offsets.

Could the merger enforce the format's offset limit and split output into separate cache files before crossing it?

I preserved the original .toc/.bin, logs, and a Python verifier. I also recovered all 126,127 records into three files below 2 GiB with rebuilt indexes and verified every key and record boundary. Your isolated-cache workaround is installed; actual in-game reuse and warm-relaunch validation are still pending. This proves the indexing defect and explains this batch of warnings, but does not yet establish how much of the repeated Fossilize processing it causes.

Eedmundselliot 2026-10-05 github

Not sure if Valve is using nvcachetools under the hood, but I noticed binoffset is cast to a u32 here: https://github.com/therontarigo/nvcachetools/blob/dfd7893f72904cc9427ca068bf2f3367d60dcd7e/nvcachedec.c#L168

And it seems like that is NVIDIA's file format. Probably Steam needs to save separate caches if size grows over UINT32_MAX, or NVIDIA needs to support u64 offsets

KKlausikPL 2026-10-05 github

Not sure if Valve is using nvcachetools under the hood, but I noticed binoffset is cast to a u32 here: https://github.com/therontarigo/nvcachetools/blob/dfd7893f72904cc9427ca068bf2f3367d60dcd7e/nvcachedec.c#L168

And it seems like that is NVIDIA's file format. Probably Steam needs to save separate caches if size grows over UINT32_MAX, or NVIDIA needs to support u64 offsets

So for now disable pre cache? Because in borderlands 4 when i do pre cache 2gb after end game start from 0% so game ignore this cache

XXerrion 2026-10-06 github

I decided to throw some tokens at GPT 6.1 Sol and turn this workaround into a small open-source tool:

https://github.com/Xerrion/steam-shader-guard

Steam Shader Guard keeps each game's NVIDIA shader cache separate from Steam's shader-file merger. It can also check and copy reusable data from existing shader files without changing the originals. A separate Steam (Shader Guard) shortcut starts Steam with NVIDIA shader replay disabled for that session.

The aim is to make the setup easier for regular Linux players. Installation, optional shader copying, and game setup are separate steps. The README and command output explain each one.

This is an experimental workaround, not a fix for Steam's merger. It cannot recreate missing shader data or guarantee that every shader will be reusable. Games may still need to compile new shaders. It currently supports the regular Linux Steam app with NVIDIA graphics, not Flatpak or Snap.

MMaxterious 2026-10-07 github

I have the same problem.

MMaxterious 2026-10-07 github
KKlausikPL 2026-10-08 github

I am not sure if this might fix your problem?
https://wiki.cachyos.org/configuration/gaming/#increase-maximum-shader-cache-size

I do this
I mean in my case shader pre cache not working(see my pinned error above in other issue) many mismtched key and foz header truncated
On borderlands 4 few gb of shader pre cached but NVIDIA create own shader in this same file (don't started from scratch but writed next shader to same file) instead load it so game start from 0% compiling shaders but after compile them working normally

SSammMartins 2026-10-21 · hidden on GitHub github

Additional reproduction: X4: Foundations repeatedly re-queues the same shader depot on NVIDIA

I am experiencing a related issue on Omarchy Linux (Arch-based) with a native Steam installation and an NVIDIA GeForce GTX 1060 6 GB.

System information

  • Distribution: Omarchy Linux (Arch-based)
  • Steam package version: 1.0.0.87-3
  • GPU: NVIDIA GeForce GTX 1060 6 GB
  • NVIDIA driver: 580.178.04
  • Game: X4: Foundations (AppID 392160)
  • Steam library: /data/SteamLibrary
  • Filesystem: Btrfs, persistent and writable
  • Shader pre-caching and background Vulkan shader processing: enabled

Observed behavior

Steam repeatedly downloads and processes approximately 3 GB of Vulkan shader data for X4: Foundations. The issue is reproducible without rebooting the system.

The shader cache directory already exists and occupies approximately 6.1 GB:

/data/SteamLibrary/steamapps/shadercache/392160

The cache contains fozpipelinesv6, nvidiav1, DXVK_state_cache, downloads, and temp.

Relevant shader_log.txt evidence

At 2026-10-20 22:16:34, Steam successfully completed the shader depot download/commit for manifest 8760948818145062529 (3,035,128,149 bytes).

During the subsequent replay step, Steam removed the native pipeline depot file steam_pipeline_cache.foz. At 22:16:36, it queued the identical manifest again. The same manifest was queued again after X4 exited at 22:18:01.

This appears to be a repeatable shader depot/cache-manager loop rather than a game-content update.

Checks performed

  • /data is persistent, writable Btrfs.
  • Cache ownership and ACLs are correct.
  • The game installation is not being downloaded again.
  • Steam shader settings remain enabled.
  • No startup script or configuration override was found that explains the behavior.
  • NVIDIA driver and Steam versions were recorded.
  • No cache files, game manifests, or Steam settings were modified during diagnosis.

The shader log also contains RegisterShader responses with Busy (10), but the repeated manifest queueing occurs independently of those messages.

Could this be related to the NVIDIA shader cache merge/replay behavior described in this issue? In particular, is there a known reason why Steam removes the native pipeline cache during replay and immediately queues the same committed depot manifest again?

I can provide the relevant log excerpts and additional diagnostics if needed.

Yyotamleo 2026-10-23 github
Confirming the 4 GiB wraparound from @edmundselliot's comment, now on a fully stock shader setup (Overwatch 2, RTX 4090, nvidia-open 615.78.08, Steam client 1788652215, proton-cachyos 11.0-20261015). No `__GL_SHADER_DISK_CACHE_*` or other shader env vars, no custom cache path, no `dxvk.conf`, no `steam_dev.cfg`; Steam's own shadercachemanager is the only thing touching the cache. The same bug shows up with CS2 (native, no Proton) above, so the Proton build shouldn't matter.

After one stock session, the exit merge wrote steamapp_merged_shader_cache.bin at 5,370,772,223 bytes (5.00 GiB), with a .toc of 262,547 entries.

Checking each TOC entry's 16-byte key against the section header at its stored offset:

  • 211,874 entries match at the stored offset
  • 50,673 entries (19 %) match only at stored offset + 2^32
  • 0 entries are unmatched

The first wrapped entry is index 211,874, the one right after the last good entry, whose section ends at 4,294,973,767, just past 2^32. Every record is physically intact and only the 32-bit offset in the .toc overflowed.

The cycle is visible across one session:

Merge .bin size Wrapped entries
Steam restart, before playing 4,403,315,492 3,444 of 203,109
Game launch 6,170,928,386 (replaced before I could check)
Game exit, after adding a new 1,075,803,069-byte fragment 5,370,772,223 50,673 of 262,547

The exit merge came out 1.87 GB smaller than the launch merge plus the new fragment. shader_log.txt logged "Mismatching key!" at least 131,769 times between 21:19:44 and 21:20:01, during the merge and during the upload pass that reads the new merged file. The log hit its 4 MB rotation twice in that window, so the real count is higher. So the merger drops what it can't find through the wrapped offsets, the file shrinks, the next session pushes it over 4 GiB again, and the cycle repeats. Those dropped shaders presumably get compiled again in the next session.

The TOC entry's offset field is 4 bytes, so a single merged file can't address more than 4 GiB. A fix on the Steam side would be to stop merging at 4 GiB and start a second merged file (steamapp_merged_shader_cache1.bin), or to keep the per-fragment files instead of merging past that size.

The checker is about 30 lines of Python:

import mmap, struct, sys

toc_path, bin_path = sys.argv[1], sys.argv[2]
toc = open(toc_path, "rb").read()
f = open(bin_path, "rb")
b = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
size = len(b)
n = (len(toc) - 0x20) // 0x18
ok = wrap = bad = 0
first_wrap = None
max_ok_end = 0
for i in range(n):
    e = toc[0x20 + i * 0x18: 0x20 + (i + 1) * 0x18]
    key, off, sz = e[:0x10], struct.unpack("<I", e[0x10:0x14])[0], struct.unpack("<I", e[0x14:0x18])[0]
    if b[off + 4:off + 0x14] == key:
        ok += 1
        max_ok_end = max(max_ok_end, off + sz)
    elif off + 2**32 + 0x14 <= size and b[off + 2**32 + 4:off + 2**32 + 0x14] == key:
        wrap += 1
        if first_wrap is None:
            first_wrap = i
    else:
        bad += 1
print(f"bin={size} ({size / 2**30:.2f} GiB) entries={n} ok={ok} wrapped_by_2^32={wrap} unmatched={bad}")
print(f"first wrapped entry index={first_wrap} max matched end offset={max_ok_end} (2^32={2**32})")

Run it as python3 tocwrap.py steamapp_merged_shader_cache.toc steamapp_merged_shader_cache.bin in shadercache/<appid>/nvidiav1/GLCache/<driverhash>/<hash>/.

Yyotamleo 2026-10-23 github
Follow-up with two more results from the same machine:
  1. An isolated driver cache stays warm. With __GL_SHADER_DISK_CACHE_PATH/_APP_NAME pointing at a private dir (so the merger never touches it), the first launch wrote 2.46 GB of new cache. The relaunch wrote 0 bytes in its first 3 minutes and 65 MB over a whole match. The driver's own cache handles size by itself (it rolls overwatch1.bin etc.); the wraparound only happens in Steam's merged file.

  2. Part of Steam's precompiled NVIDIA cache doesn't match what the game needs. I recovered all 262,547 entries of the merged file (including the 50,673 wrapped ones) into shards under 2 GiB and gave them to the game read-only. The driver opened them, but the game still recompiled 50,164 keys from the first shard. For those keys, the record the game wrote has the same 16-byte TOC key but different header bytes 20..28, a different size and a different payload (2,000 of 2,000 sampled pairs). So beyond the offset overflow, some precompiled entries are compiled differently from what Overwatch.exe produces, perhaps because fossilize_replay doesn't get the game's driver application profile. That would explain recompiles even below 4 GiB.

Yyotamleo 2026-10-24 github

I published the scripts behind my measurements above, in case they help others confirm the bug on their own cache or help Valve reproduce it: https://github.com/yotamleo/nvidia-steam-shader-cache (MIT, Python stdlib + bash).

  • tools/tocwrap.py <merged.toc> <merged.bin> checks whether your merged file is past the 4 GiB wrap. Mine: entries=262547 ok=211874 wrapped_by_2^32=50673 unmatched=0.
  • tools/split.py recovers all entries, wrapped ones included, into verified parts under 2 GiB.
  • tools/reccmp.py compares records that share a key across two caches. That is how I found Steam's precompiled entries differ from what the game compiles under the same key.

For an actual fix, @Xerrion's steam-shader-guard packages the isolated-cache approach for any game.

Yyotamleo 2026-10-24 github

@kisak-valve could this be relabeled from NVIDIA drivers to a Steam client label? The evidence in this thread points at Steam's shadercachemanager, not the driver:

  • The 4 GiB limit is the NVIDIA cache file format (a .toc entry stores the .bin offset as a u32). The driver writes its own cache as separate per-app files (<app>0.bin, <app>1.bin, …).
  • Steam's merge builds one file past 4 GiB. The merged steamapp_merged_shader_cache.bin reaches 5+ GB, entries beyond 2^32 get wrapped offsets, and the merger itself logs Mismatching key! and drops them at the next merge. That reproduces on two independent machines with byte-level checks (CS2 native by @edmundselliot, Overwatch 2 under Proton by me: 50,673 of 262,547 entries found only at stored offset + 2^32, 0 unmatched).
  • The same driver works when Steam's merger is kept out of it. With a private __GL_SHADER_DISK_CACHE_PATH, the relaunch wrote 0 bytes of new cache in its first 3 minutes.

A client-side fix would be to cap each merged file below 4 GiB and roll over to a second file. Splitting the merged file into parts under 2 GiB recovers every entry intact (tools/split.py in the repo below). The checker I used is public if it helps reproduce: https://github.com/yotamleo/nvidia-steam-shader-cache/blob/main/tools/tocwrap.py

SShayano 2026-10-28 github

Third byte-level reproduction, on a newer Steam client build than the ones above (1792618285). Same setup as @yotamleo: Overwatch 2 (2357570), RTX 4090, nvidia-open 615.78.08, proton-cachyos-slr, no __GL_SHADER_DISK_CACHE_* variables set by me.

After a game session, steamapp_merged_shader_cache.bin is 5,370,782,366 bytes (5.00 GiB). Checking every .toc key against the record header at its stored offset:

entries: 194719, ok: 159513, wrong key at stored offset: 35206, found at stored offset + 4 GiB: 35206

Steam then logged 159513 shaders for that bucket, i.e. only the entries before the wrap, and the merge wrote 66,075 Mismatching key! lines, enough to rotate shader_log.txt within seconds. Background processing has since written more than 5 GB of new fragments and is still running, so the next merge will be well over twice the limit.

+1 for moving this to a Steam client label: capping each merged file below 4 GiB and rolling over to a second file would avoid it.