Quick answer: The save file stores the old class path. After a rename, the deserialization cannot find the class. Add a CoreRedirect in DefaultEngine.ini to map the old class name to the new one, and existing save files will load correctly.

Here is how to fix Unreal SaveGame not loading after a class rename. You shipped your game with a UMySaveGame class. Players have save files. You renamed the class to UPlayerSaveData during a refactor, and now UGameplayStatics::LoadGameFromSlot returns nullptr for every existing save file. New saves work fine, but all existing player progress is gone. This is a solved problem in Unreal — CoreRedirects exist specifically for this scenario.

The Symptom

LoadGameFromSlot returns nullptr. The save file exists on disk (you can find it in the SaveGames directory). New save files created after the rename load correctly. Only saves created before the rename fail. The log may show a warning about a missing class or struct during deserialization.

This also happens when you rename UPROPERTY fields, move the class to a different module, or rename the module itself. Any change to the serialized name breaks backward compatibility with existing save files.

What Causes This

1. Class path mismatch. Unreal’s serialization stores the fully qualified class path: /Script/MyGame.MySaveGame. When you rename the class to PlayerSaveData, the path becomes /Script/MyGame.PlayerSaveData. The old save file still references MySaveGame, which no longer exists. Deserialization fails.

2. Property name mismatch. Serialized properties are stored by name. If you rename PlayerLevel to CharacterLevel, the old value under PlayerLevel has no target property in the new class. The data is silently discarded.

3. Module rename or move. Moving a class from module MyGame to module MyGameCore changes the package path. The old save references /Script/MyGame.MySaveGame but the class now lives at /Script/MyGameCore.MySaveGame.

The Fix

Step 1: Add CoreRedirects to DefaultEngine.ini.

; DefaultEngine.ini
[CoreRedirects]
; Class rename
+ClassRedirects=(OldName="MySaveGame",NewName="PlayerSaveData")

; Property rename within the class
+PropertyRedirects=(OldName="PlayerSaveData.PlayerLevel",NewName="CharacterLevel")

; Module/package rename
+PackageRedirects=(OldName="/Script/MyGame",NewName="/Script/MyGameCore")

; Struct rename
+StructRedirects=(OldName="InventorySlot",NewName="ItemSlotData")

; Enum rename
+EnumRedirects=(OldName="EWeaponType",NewName="EEquipmentType")

Step 2: Verify the redirect works. Load an old save file and check that all properties deserialize correctly:

void AMyGameMode::TestSaveLoad()
{
    USaveGame* LoadedSave = UGameplayStatics::LoadGameFromSlot(
        TEXT("TestSlot"), 0);

    if (!LoadedSave)
    {
        UE_LOG(LogTemp, Error, TEXT("Load failed - check CoreRedirects"));
        return;
    }

    UPlayerSaveData* SaveData = Cast<UPlayerSaveData>(LoadedSave);
    if (!SaveData)
    {
        UE_LOG(LogTemp, Error, TEXT("Cast failed - class redirect wrong"));
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("Level: %d, Gold: %d"),
        SaveData->CharacterLevel, SaveData->Gold);
}

Step 3: Add a version number to your save data. Future-proof your saves by including a version field that lets you handle migrations in code:

UCLASS()
class UPlayerSaveData : public USaveGame
{
    GENERATED_BODY()

public:
    UPROPERTY()
    int32 SaveVersion = 2;

    UPROPERTY()
    int32 CharacterLevel = 1;

    UPROPERTY()
    int32 Gold = 0;

    virtual void Serialize(FArchive& Ar) override
    {
        Super::Serialize(Ar);

        // Handle migration from older save versions
        if (SaveVersion < 2)
        {
            // Migrate data from v1 format
            Gold = Gold * 100; // v1 used dollars, v2 uses cents
            SaveVersion = 2;
        }
    }
};

“CoreRedirects are not just for saves. They work for any serialized data: Blueprints, DataTables, Level assets. If you renamed anything and something stopped loading, a redirect is the answer.”

Why This Works

Unreal’s serialization system processes CoreRedirects before attempting to resolve class and property names. When the deserializer encounters MySaveGame in a save file, it checks the redirect table, finds the mapping to PlayerSaveData, and uses the new class. The redirect is transparent — no special code needed, no data migration. The save file still contains the old name, but the runtime maps it correctly. Redirects must stay in your config permanently because you cannot control which version of the save a player might have.

Related Issues

If your save file loads but DataTable row references inside it resolve to nullptr, see Fix: Unreal DataTable Row Not Found at Runtime for FName resolution issues that affect saved row references.

For Blueprint class renames that break level actors, the same CoreRedirect system applies. Add a ClassRedirect for the Blueprint path: +ClassRedirects=(OldName="/Game/BP/BP_OldName",NewName="/Game/BP/BP_NewName").

Rename the class, add the redirect. Remove the redirect, lose the saves. Keep them forever.