LorisYounger/VPet · error · Exception

Save data is null

Error message

Save data is null

What it means

MainWindow.Save serializes GameSavesData to LPS and asserts the resulting string is non-null before writing the save file, throwing a generic Exception if it is null. This is a defensive invariant protecting the save routine from silently writing nothing.

Solutions

  1. Ensure GameSavesData is initialized before Save/autosave can run (guard AutoSaveTimer start until data load completes).
  2. Verify ToLPS()/ToString() never returns null (return empty LPS instead).
  3. Skip the save when GameSavesData is null and log instead of throwing, so window close isn't blocked.
  4. Repair or delete the corrupt save that produced a null model, then let the game recreate it.

Example fix

// before
var savesdata = saveslps.ToString();
if (savesdata == null)
    throw new Exception("Save data is null");
// after
if (GameSavesData == null) return; // nothing to save yet
var savesdata = saveslps?.ToString() ?? string.Empty;
Defensive patterns

Strategy: try-catch

Validate before calling

if (GameSavesData == null) { /* skip save */ }

Type guard

bool CanSave() => GameSavesData != null && GameSavesData.ToLPS() != null;

Try / catch

try { Save(); } catch (Exception ex) when (ex.Message == "Save data is null") { Log.Warn("save skipped: no game data loaded yet"); }

Prevention

When it happens

Trigger: Save (called by Restart_Closed, AutoSaveTimer_Elapsed, Window_Closed) runs while GameSavesData.ToLPS() yields a null string — effectively a null/degenerate save data model.

Common situations: Autosave timer firing before the game data model was initialized; closing the window during early startup before GameSavesData is populated; corrupted/missing save data loaded as null.


AI-assisted analysis of LorisYounger/VPet@ffb9cc2a85 (2026-09-15). Data as JSON: /api/errors/902c79ab4de0e349. Report an issue: GitHub.

Appendix: source

Thrown at VPet-Simulator.Windows/MainWindow.cs:327

                    var ds = new List<string>(Directory.GetFiles(ExtensionValue.BaseDirectory + @"\Saves", $"Save{PrefixSave}_*.lps")).OrderBy(x =>
                    {
                        if (int.TryParse(x.Split('_').Last().Split('.')[0], out int i))
                            return i;
                        return 0;
                    }).ToList();
                    while (ds.Count > Set.BackupSaveMaxNum)
                    {
                        File.Delete(ds[0]);
                        ds.RemoveAt(0);
                    }

                    if (File.Exists(ExtensionValue.BaseDirectory + $"\\Saves\\Save{PrefixSave}_{st}.lps"))
                        File.Delete(ExtensionValue.BaseDirectory + $"\\Saves\\Save{PrefixSave}_{st}.lps");

                    var saveslps = GameSavesData.ToLPS();
                    var savesdata = saveslps.ToString();
                    if (savesdata == null)
                        throw new Exception("Save data is null");


                    int hash = Math.Abs(saveslps.GetHashCode() % 255);
                    if (File.Exists(ExtensionValue.BaseDirectory + $"\\Saves_BKP\\Save{PrefixSave}_{hash:X}.lps"))
                        File.Delete(ExtensionValue.BaseDirectory + $"\\Saves_BKP\\Save{PrefixSave}_{hash:X}.lps");

                    //存档
                    File.WriteAllText(ExtensionValue.BaseDirectory + $"\\Saves\\Save{PrefixSave}_{st}.lps", savesdata);
                    //备份
                    File.WriteAllText(ExtensionValue.BaseDirectory + $"\\Saves_BKP\\Save{PrefixSave}_{hash:X}.lps", savesdata);

                    if (File.Exists(ExtensionValue.BaseDirectory + @"\Save.lps"))
                    {
                        if (File.Exists(ExtensionValue.BaseDirectory + @"\Save.bkp"))
                            File.Delete(ExtensionValue.BaseDirectory + @"\Save.bkp");
                        File.Move(ExtensionValue.BaseDirectory + @"\Save.lps", ExtensionValue.BaseDirectory + @"\Save.bkp");
                    }

View on GitHub (pinned to ffb9cc2a85)