Verse Wiki — the Verse handbook for Blueprint authors
Bonus · EXTRA

A Version-Evolution Pitfall, Documented: The persistable class You Can't Delete After Publishing

A creator tried to delete a long-abandoned persistable class — and the project could never be published again. This real forum case is the perfect cautionary tale for "design your save structure before your first publish". This article replays the incident, explains the why, and lays out a retirement plan that actually works.

1. The Scene of the Incident: Delete the Old Class, Publishing Fails

The forum thread "Cannot Remove an Old Persistable Class from a Previously Published UEFN Project" captures the scene: an early version of the author's project had a persistable class named CustomPlayer, plus its matching save table (weak_map); after a system refactor, none of it was used anymore. While cleaning up, he deleted the class definition and that save table together — Compile passed fine locally, but publishing failed, with an error pointing to "a definition that existed in the previous version is missing".

The Epic staff reply stings even more: this is not a bug — it's intended behavior. Every re-publish of a published project runs a backward-compatibility check between your new code and the live version, and removing a persistable class from a published project is the textbook incompatible change. The thread also mentions Epic planned to relax this restriction in a later release (34.40) — check the latest official notes for whether that has landed.

2. Why It Can't Be Deleted: What the Compatibility Check Is Checking

Intuitively: "I don't even use this class anymore — who does deleting it hurt?" It hurts the player saves already living in production. Persistent data hangs off player accounts, storing piles of records laid out in the old class's structure; the publish-time backward-compatibility check is essentially re-verifying that save table's "what type goes in each slot" — it must guarantee that every old save can still be read correctly under the new version. With the type definition gone, that data becomes orphans nobody recognizes, and the engine's stance follows the same lineage as the base lesson's "load failed, entry denied": better to block your publish than to let saves rot into junk data.

The same logic explains another fact: persistent data can never truly be deleted, only reset — the defaults written back over it. Once a record is on the books, it can never be struck off the register.

3. A Retirement Plan That Works: The Old Class Stays Behind, the New System Opens Its Own Doors

The countermeasure the community settled on is humble: leave the old definition in the code, retired but never, ever deleted; the new system starts fresh with its own parallel save table (weak_map). Like the snippet below — the upper half puts the old class into "retirement on full pay", the lower half opens a brand-new save table for the new system:

retire_old_class.verse
# Old class: already shipped in a published version; deleting it breaks publishing
# Let it retire on full pay — keep the definition, stop using it in code
custom_player := class<final><persistable>:
    Score:int = 0

var OldCustomPlayers:weak_map(player, custom_player) = map{}

# New system: open a separate persistent variable in parallel
player_profile_v2 := class<final><persistable>:
    Score:int = 0
    Level:int = 1

var ProfilesV2:weak_map(player, player_profile_v2) = map{}

Mind the price tag: each project has a hard cap on save tables (currently 4), and the retired weak_map keeps squatting on one slot for free. That's exactly why this case belongs in a textbook — every time your save structure "opens another table", it burns budget you can never get back. Incidentally, if all you want is to move old data to a new home, you can read the old save table once when a player joins, write into the new save table, then reset the old record to defaults — a slow-and-steady migration.

Set yourself three rules. One: before your first publish, run your save structure through several rounds on paper — if one class with room to evolve can do the job, don't open a second persistent variable. Two: design persistable classes as add-only from day one — fields get added, never removed. Three: for any refactor touching save data, verify the compatibility check on a private release first, then touch the real version.

Your published project has an unused persistable class old_stats, and you want to overhaul the save structure. What's the right move?

Sources

This article draws on the Epic Developer Forums (including a confirming reply from Epic staff):