the symptom, in your words

"Static registry populated by a static constructor doesn't pick up new entries on hotload"

✓ verified on engine 26.07.08elane: Tooling & environmentposted

▸ SYMPTOM

You add a new entry to a static registry (populated by a static constructor or static field initializer), and:

  • compile_status is green, dotnet build is green
  • Method code hotloads and runs correctly
  • But the new registry entry is absent at runtime — Registry.Get("newItem") returns null

The killer diagnostic: an entry added earlier (already present at the last cold load) resolves fine, while the newly-added adjacent entry is missing. Only a pre-addition static constructor produces that split.

This is distinct from hotload-stuck-stale-restart-required — there, method code is also stale. Here, methods hotload fine; only the static data is frozen.

▸ CAUSE

s&box's hotload preserves static state across reloads. It replaces method bodies but never re-runs static constructors (static ClassName()) or static field initializers. The registry was populated at the last cold load and its dictionary/list/set still contains only those entries.

▸ FIX

A full editor process restart (quit + relaunch) so the assembly cold-loads and the static constructor runs against current source.

An mtime-bump, hotload, or play_stop + play_start will NOT clear it — statics survive play restarts.

Trap shape to watch: registry-validated operations fail (anything gated on Registry.Get(id) != null) while raw-string uses of the same ID keep working. For example, code that stores itemId = "hammer" and uses it as a string succeeds, but the registry-backed lookup for metadata (icon, display name) returns nothing. A feature can appear fully playable while its registry-metadata lookups render blank or its validation verbs error.

Verification gate: after adding a registry entry, prove it live via a registry-validated path (a Get-backed lookup) before trusting it. Green compile + working gameplay is not proof the registry data is current.

▸ WHY IT WORKS

Cold-loading the assembly runs all static initializers in declaration order, populating the registry from the current source. Hotload is designed to preserve runtime state (game objects, scores, positions) across code changes — static constructors are part of "state initialization" that the engine deliberately skips to avoid resetting game state. The tradeoff is that static data structures require a restart to pick up structural changes.

Verified on engine 26.07.08e — seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?

Want to know when new guides or fixes drop? Join the community to help build this out — report gotchas, flag outdated fixes, or just lurk.

Join the Discord