Follow-up to Why Forza Horizon 6 Dies Three Minutes In. Same machine, same instrument, new build.
TL;DR
- We re-tested the silent crash on build 6.403.798.0 (previously reported against 6.382.893.0). It still reproduces — 4 out of 4 sessions across two builds, 100% of launches on this machine.
- The code was touched. The dominant upload-heap block size changed from 64 MB to 32 MB. Something in that allocation path was modified between builds.
- The lifetime bug is unchanged. The buffers are still never released. Halving the block size just doubled the number of blocks — same ceiling, reached sooner.
- It’s now faster. The leak starts at roughly T+33s instead of T+93s, and saturates by T+66–90s instead of T+195s. The entire failure is front-loaded into the first minute and a half.
- New symptom: it hangs first. The process now sits completely flatlined at the allocation ceiling for about 130 seconds before terminating. From the player’s side this reads as “not responding,” then a silent close — rather than the abrupt disappearance we saw before.
- Everything else is identical: exit code 0, no error dialog, no Event Log entry, no crash dump, and zero writes to the shader cache.
- The language-change workaround still works — confirmed stable past 15 minutes on the new build. It doesn’t prevent the leak; it reaches the identical ceiling and then plateaus instead of dying. Notably, that surviving session does write to the shader cache, which no crash run has ever done.
What we tested
Same rig as the original investigation, current game build:
| Game build | 6.403.798.0 (Steam) — previously 6.382.893.0 |
| OS | Windows 10 Pro 19045 |
| GPU | NVIDIA RTX 5070 Ti 16 GB, driver 32.0.16.1074 (AMD iGPU also present) |
| CPU | AMD Ryzen 7 9800X3D |
| RAM | 32 GB |
| Steam beta | Not enrolled |
| Session | 28 July 2026, 01:15:38Z → 01:19:23Z — lifetime 225s, exit code 0 |
What hasn’t changed
Every load-bearing finding from the original investigation still holds.
The exit is still silent. Code 0 — the “everything is fine” code. No dialog, no Application or System Event Log entries for the window, and therefore still no Windows Error Reporting dump. WER cannot fire on a clean exit. The game does not believe it crashed.
The shader cache still receives nothing. Zero writes to %LOCALAPPDATA%\ForzaHorizon6 across the entire run. The optimization pass still never persists its work, which is why it re-runs in full on every single launch. This is what turns a one-time failure into a permanent crash loop.
The signature is still D3D12 upload heaps. Write-combined committed memory dominates at death — 9.58 GB at the final sample, against roughly 13 MB before the pass began.
It’s still not the network. TCP connections to the online services stayed established right through to process exit. This is not the disconnect-related crash some players report separately.
It’s still not resource exhaustion. At the ceiling the machine had about 4.9 GB of system RAM free and 6 GB of VRAM headroom. The game gave up with room to spare.
What did change — and why it matters
Here’s the comparison that tells the story:
| 6.382.893.0 | 6.403.798.0 | |
|---|---|---|
| Leak onset | ~T+93s | ~T+33s |
| Ceiling reached | ~T+195s | ~T+66–90s |
| Behaviour at ceiling | Died ~20s later | Flatlined ~130s, then exited |
| Ceiling (private bytes) | ~16.4 GB | ~15.8 GB |
| Process exit | T+215s | T+225s |
The whole leak is now compressed into the first ninety seconds or so. And then something genuinely new happens: from T+66s to T+219s, every counter goes completely flat. VRAM pinned at 9,501 MB. Private bytes at roughly 15,787 MB. Handles at 8,818. Threads at 170. Region count at 18,603. Sample after sample, nothing moves.
The process is wedged at its allocation ceiling for over two minutes before it finally terminates.
That’s a meaningful change in how this presents to players. Previously the game just vanished mid-session. Now you get a hang — a “not responding” window — and then the silent close. If you’ve been seeing FH6 lock up rather than disappear and assumed that was a different problem, it probably isn’t. It’s the same bug wearing a new coat.
The forensics: 32 MB is the tell
Because the game exits cleanly, Windows never writes a dump — so we captured an external minidump mid-leak, at 13.28 GB private bytes, while the allocation was still running.
Region analysis of the committed address space:
- Write-combined: 7.60 GB across 249 regions. (Private total 12.78 GB, mapped 2.34 GB, image 0.84 GB, across 16,231 committed regions.)
- 119 identical 32 MB write-combined allocations, totalling 3.72 GB. This is the new dominant pattern.
- 14 × 64 MB blocks remain, accounting for 0.88 GB. Largest single write-combined region: 404 MB.
- 182 write-combined regions in the 16–256 MB range, totalling 6.76 GB.
The modal block size moving from 64 MB to 32 MB is the interesting part. That is not something that changes on its own. Somebody modified the upload-heap allocation path between these two builds.
But the lifetime is still wrong. Staging buffers are still allocated per shader batch and none are released. Halving the block size only halved the granularity of the leak — the pass now simply allocates twice as many blocks and arrives at the same ceiling sooner. If this was an attempted fix, it addressed block sizing rather than release, and the net effect on the player is that the game now dies faster.
The one-sentence bug report, updated: during the “optimizing shaders for your PC” pass, the game allocates D3D12 upload heaps — now predominantly 32 MB write-combined blocks — and never releases them, saturating at roughly 15.8 GB committed within about 90 seconds of the pass starting, after which the process hangs for two minutes and self-terminates with exit code 0.
The language-change workaround still works — confirmed on 6.403.798.0
Good news: the workaround survives the patch. Change the game’s language in the options menu, let the game auto-restart, and the session lives.
We ran a second instrumented session to verify it. Telemetry captured 477 seconds; the game was still running fine past the fifteen-minute mark, against a crash run that died at 225 seconds.
But — exactly as on the previous build — the restart does not stop the leak. It changes what happens when the leak finishes. Compare the two sessions on the same machine, same build:
| Crash run | Language-restart run | |
|---|---|---|
| Leak onset | ~T+33s | ~T+36s |
| Ceiling reached | ~T+66–90s | ~T+70s |
| Peak private bytes | ~15.8 GB | 15,813 MB |
| Peak write-combined | 9.58 GB | 9,583 MB |
| Peak VRAM | 9,501 MB | 9,503 MB |
| Outcome | Hung, then exit code 0 at 225s | Stable past 15 minutes |
Those ceilings are not similar, they’re the same number. The surviving session allocates just as much, just as fast, and then simply stops instead of falling over.
And it is genuinely stable, not slowly degrading. Across the plateau — T+90s to T+477s, 191 consecutive samples over six and a half minutes — private bytes drifted by 82 MB total, and VRAM by 17 MB. The last five samples are flat to within half a megabyte. Network connections held throughout, never dropping below three established. System RAM bottomed out at 4.7 GB free.
The new detail our instrument caught: this session writes to the shader cache
This is the part worth flagging to Playground.
In every crash run, across both builds, the shader cache received zero writes. That was one of the most damning findings in the original investigation — the pass never saves its work, so it re-runs in full on every launch, forever.
The surviving session is different. We logged fifteen separate write events, peaking at 26 writes in a single two-second sample, with the cache file count ticking up from 151 to 152. The writes cluster between T+2s and T+74s — right through the leak phase.
So the restarted run persists something the crashing run never does.
Two honest caveats before anyone over-reads that. The cache size stayed pinned at 14.8 MB throughout, and only one file was added — so these may be index or metadata writes rather than substantial compiled shader blobs. And this is one surviving session against four crashes; it’s a strong signal, not a proven mechanism. But it’s the first hard evidence we have that the pass can reach the point of committing work, and that whatever wedges a cold launch happens before that point.
Practical caveats if you’re using this
- You’ll likely need to redo it after game or driver updates re-trigger the optimization pass.
- It holds roughly 9.5 GB of VRAM for the entire session. On our 16 GB card that’s fine. On a smaller GPU, that plateau alone may exceed what you have — the workaround may not save you.
- It is a workaround, not a fix. The leak is still happening every time you launch.
What we’d still ask Playground for
The engineering asks from the original investigation stand, with one addition and a sharper point on the first.
- Fix the upload-heap lifetime, not the block size. The dump shows staging buffers allocated per batch and never released. Release each heap once its copy completes, or better, reuse a small ring of them. Changing 64 MB to 32 MB did not address this — it made the failure arrive sooner.
- Bound the pass. Compile in batches against a memory budget and release intermediates as you go. A per-PC optimization step should never be able to commit 16 GB.
- Persist progress — and look at why the restarted session can. A cold launch writes nothing to the shader cache before dying, so a crash means starting from zero forever. But the language-restart session does write, fifteen times, while leaking identically. Whatever gates those writes on a normal launch is worth tracing: it may be the same wedge that turns a survivable plateau into a crash.
- Fail loudly. Exit code 0 on an allocation failure suppresses the crash dialog, the Event Log entry, and WER capture simultaneously. It is the single biggest reason this bug has been hard to triage — and it’s fixable independently of the leak itself.
- New: investigate the two-minute hang. The process now sits at its ceiling with every counter frozen for 130 seconds before exiting. Whatever loop it’s stuck in during that window is likely to be diagnostic gold, and it’s a worse player experience than the fast crash it replaced.
The bottom line
A build shipped, the allocation path changed, and the bug survived. That’s not nothing — it tells us somebody is in that code. But the change addressed the symptom’s granularity rather than its cause, and the practical result for affected players is a game that now fails in half the time and locks up for two minutes on its way out.
We’ve replied in-thread on the existing Forza Support ticket with the full telemetry, the memory-map analysis, and an offer to run instrumented builds. This reproduces on 100% of launches on this machine, so turnaround on any test they want run is fast.
If you’re hitting this, the most useful thing you can do is file a ticket with data rather than a description. “Crashes after a few minutes” gets a template reply. Build number, exit code, memory figures, and a telemetry capture get escalated.
Previously: Why Forza Horizon 6 Dies Three Minutes In — the original investigation, including why deleting your shader cache and raising the NVIDIA driver cache do nothing.
Also relevant: FH6 Keeps Crashing on Startup? — a different crash, if yours happens before the main menu.
Leave a comment