How to fix a multiplayer save that refuses to load because Steam deleted
(or you removed) a Workshop mod, without regenerating
WorldDictionary.bin and scrambling item IDs. Written from a
real Planet Express / Buildable Roofs case.
Clients fail while joining. Server logs (or the client console) show:
WorldDictionaryException: [SpriteConfigs] Missing dictionary script on client: Base.SomeModThing
WorldDictionary: Cannot load world due to WorldDictionary error.
The save still marks obsolete SpriteConfig scripts as loaded. The server may tolerate stale entries; clients do not.
| Step | Action |
|---|---|
| 1 | Stop the server. Back up the whole world folder (at least WorldDictionary.bin + WorldDictionaryReadable.lua). |
| 2 | In the SCRIPTS section of WorldDictionaryReadable.lua, identify obsolete entries with isLoaded = true. |
| 3 | In WorldDictionary.bin, flip each matching script load flag from 0x03 to 0x00 (same length, same IDs). |
| 4 | Remove the dead Workshop ID from WorkshopItems=. Clients cannot download a delisted item. |
| 5 | Drop that Mods= id, or point WorkshopItems at a Steam-live successor if you have one. Start and join. |
Under the SCRIPTS section header in WorldDictionaryReadable.lua,
entries look like:
{
registryID = 247,
name = "Base.CFBEAutomaticBrownRoofBottom10WidthFirst",
version = "4035058024937495098",
isLoaded = true,
},
Those isLoaded = true scripts are what every client must still
provide. For a dead mod, unload them in the binary (optionally mirror
isLoaded = false in the readable dump).
Names are stored without the Base. prefix when the fulltype
starts with Base.. Immediately before the name:
| Field | Size | Notes |
|---|---|---|
| flag | 1 byte | 0x03 = loaded, 0x00 = unloaded |
| registryID | 2 bytes | big-endian; matches readable registryID |
| version | 8 bytes | big-endian signed (>q); matches readable version |
| name length | 2 bytes | big-endian |
| name | UTF-8 | e.g. CFBEAutomaticBrownRoofBottom10WidthFirst |
Patch only the flag byte. Do not rewrite names or versions unless you intentionally remap to another existing script of the same string length.
Stop the server first. Change PREFIX to your dead mod’s
script prefix.
from pathlib import Path
import re
import struct
world = Path("path/to/Saves/Multiplayer/YourWorld")
bin_path = world / "WorldDictionary.bin"
readable = world / "WorldDictionaryReadable.lua"
# Scripts that must no longer be required on clients:
PREFIX = "Base.CFBE" # change to your dead mod's script prefix
data = bytearray(bin_path.read_bytes())
text = readable.read_text(encoding="utf-8", errors="replace")
marker = "--[[ ---- SCRIPTS ---- --]]"
scripts = text[text.find(marker) :]
pattern = (
r'\{\s*registryID\s*=\s*(\d+)\s*,\s*name\s*=\s*"([^"]+)"\s*,'
r'\s*version\s*=\s*"(-?\d+)"\s*,\s*isLoaded\s*=\s*(true|false)\s*,\s*\}'
)
entries = []
for match in re.finditer(pattern, scripts):
name = match.group(2)
if not name.startswith(PREFIX):
continue
entries.append(
{
"id": int(match.group(1)),
"name": name,
"version": int(match.group(3)),
"loaded": match.group(4) == "true",
}
)
patched = 0
for entry in entries:
short = entry["name"][5:] if entry["name"].startswith("Base.") else entry["name"]
utf = short.encode("utf-8")
pat = struct.pack(">H", len(utf)) + utf
offset = 0
while True:
found = data.find(pat, offset)
if found < 0:
raise SystemExit(f"not found in bin: {entry['name']}")
version = struct.unpack(">q", data[found - 8 : found])[0]
if version != entry["version"]:
offset = found + 1
continue
flag_at = found - 8 - 3
registry_id = struct.unpack(">H", data[found - 8 - 2 : found - 8])[0]
assert registry_id == entry["id"], (entry, registry_id)
if data[flag_at] == 0x03:
data[flag_at] = 0x00
patched += 1
break
bin_path.write_bytes(data)
print(f"unloaded {patched} scripts")
Objects from the dead mod may be invisible, broken, or inert. This patch restores loadability, not a perfect migration.
Keep the backup until you have played a session and checked loot and vehicles.
A Workshop rewrite of the same mod is a new item — do not expect old constructions to map cleanly.
Legacy 3802011026 was taken down. Saves still required
hundreds of Base.CFBE… SpriteConfig scripts.
Unloading those flags, then pointing WorkshopItems at the
author’s V2 item 3805628478 (same Mod ID
Buildable_Roofs_Garage_Doors), let Steam clients download
again without a full dictionary regen.
Lead with the error string so search finds the guide. Call out the do-not-delete-the-bin rule early — that is where most people brick the save.