a9's thoughts on stuff

Whatever comes to mind. Probably C++.

Rambling on some Cyberpunk 2077 crashes I get

Posted at — Nov 4, 2025

Because I made the unfortunate choice of making mods for Cyberpunk 2077 and the even more unfortunate choice of making rather involved mods for it, I’ve gotten a bunch of crash dumps over the past one and a half years or so.

Usually the culprit of the crash is unrelated to my stuff, which stings given crash dump analysis without source code or debug symbols takes a bunch of reading assembly/C pseudocode.

Disclaimer: this article requires a bit of understanding of how Cyberpunk 2077 works.

Update 04.01.2026: added some notes on more recent crashes.

The world::StreamingWorld::PostLoad() unloaded streamingblock crash

One of my least favorite ones, as I have no idea what really causes it. The crash happens because sometime between .streamingworld serialization and PostLoad being called a .streamingblock resource reference gets unloaded. This generally only happens when a new game starts.

world::StreamingWorld::PostLoad will attempt to get handles to all streaming blocks contained in the streaming world resource with this:

v5 = (_QWORD *)res::ResourceReference::Get(*(_QWORD *)(this + 112) + 24 * v4);

In res::ResourceReference::Get, res::ResourceReference::EnsureLoaded will check if the resource is present. If the resource is not present, it will block the thread until the resource is loaded which will cause an assertion failure if the blocking load is not done on the game’s main thread (which it probably isn’t).

In a default, unmodded game only two .streamingblock resources (and only one if you don’t have Phantom Liberty) will be present in a .streamingworld. However, ArchiveXL adds a way for mods to append custom .streamingblock resources to the world during the loading process to make the game’s world extendable without conflicts between mods modifying the same files.

At first I blamed other mods using broken .streamingblock resources that fail to load for whatever reason, but it turns out that properly done mods can cause the crash as well. In addition, ArchiveXL’s logs don’t show streaming blocks failing to load. My only advice in this case is to remove all world editing mods before starting a new game, then reinstalling them after making your first save, be it manual or automatic.

The gameplay logic package add crash

An odd one. At first I thought it was actually my fault, as New Game+ will liberally apply abilities to bad guys - which, at the end of the day, will end up somewhere in the “add gameplay logic package” pipeline.

Freezing & eventually triggering OOM when many enemies trigger Optical Camo

An even weirder crash. When several enemies triggered Optical Camo (for instance, when you started combat by breaking stealth or the like) the game would freeze and start allocating lots of memory. Eventually you’d crash due to OOM, with the blamed memory pool being PoolGMPL_Effect if I remember right. The broken function could probably be tracked down as the crash report did spew the size and alignment of the allocation. I think the allocated struct was game::EffectInstance.

However back then my reversing brain wasn’t good enough and I simply decided to disable New Game+ providing enemies Optical Camo as a random modifier (some random encounter bosses still got it, because one enemy triggering camo never crashed things). That stopped the crash, but I never did find the root cause 😞

Allocator crashes of various flavors

These are much rarer than the upper ones, but I’ve had a bunch with the latest mod I was working on (mainly due to my own mistakes). Allocator crashes are odd in the sense that the crash can seemingly be absolutely bogus, for instance: object vtable gets overwritten with data from some resource with predictable results.

One type of allocator-related crash was due to me mishandling script_ref type in Redscript. Thankfully, that one popped up pretty quickly after the game’s launch and I was able to fix it without too many problems and debugging along the way.

Another kind was way trickier and involved the free list of a per-thread slab allocator getting corrupted. I was completely failing to reproduce the crash on my machines despite my best efforts until I built a test harness for the mod that spammed calls way faster than the mod would see during common use. With the test harness, crashes started popping up much quicker. After taking a hundred looks at everything, explaining the problem to ChatGPT a rubber duck and whining on Discord, I found two potential culprits: a bad implementation of Python’s str.join and a bad reverse of game::JournalManager::GetParentEntry which might have broken reference counts on the returned parent entry handle. After fixing both of the issues, the crashes stopped popping up and I was able to play the game with the test harness active for a while. I still don’t know which of the issues caused this bug and I’m not particularly keen on finding out.