What's wrong
In AppDataStorage/AppData.cs, AppData<T>.LoadOrCreate(...):
newAppData = JsonSerializer.Deserialize<T>(jsonString, AppData.JsonSerializerOptions)!;
newAppData.Subdirectory = subdirectory;
"null" is valid JSON. JsonSerializer.Deserialize<T>("null", ...) returns null without throwing JsonException for a reference type T. The ! only suppresses the compiler warning — at runtime newAppData really is null, and the very next line dereferences it, throwing NullReferenceException. Because no JsonException was thrown, the existing catch (JsonException) recovery path (fall back to the .bk backup, then create a fresh instance) never runs.
Why it matters / concrete failure scenario
Any settings file whose content is (or has been overwritten/truncated/corrupted to) the literal text null permanently breaks LoadOrCreate() for that app with an unhandled crash, instead of falling back to the backup or a fresh instance the way every other malformed-content case already does. This is a realistic corruption shape (e.g. a half-written file, or a writer that persists null for a missing root) that isn't covered by the existing tests — TestLoadOrCreateHandlesCorruptFile and TestLoadOrCreateRecoversFromCorruptBackup both use syntactically invalid JSON, which does throw JsonException and is handled correctly; the valid-but-null case is untested and unhandled.
Suggested fix
Treat a null deserialization result the same as a JsonException, e.g.:
newAppData = JsonSerializer.Deserialize<T>(jsonString, AppData.JsonSerializerOptions)
?? throw new JsonException("Deserialized settings file to null.");
inside the existing try block, so it flows into the current backup/fresh-instance recovery logic.
Acceptance criteria
- A test where the settings file contains the literal text
null results in LoadOrCreate() recovering (via backup or a fresh instance) instead of throwing.
What's wrong
In
AppDataStorage/AppData.cs,AppData<T>.LoadOrCreate(...):"null"is valid JSON.JsonSerializer.Deserialize<T>("null", ...)returnsnullwithout throwingJsonExceptionfor a reference typeT. The!only suppresses the compiler warning — at runtimenewAppDatareally isnull, and the very next line dereferences it, throwingNullReferenceException. Because noJsonExceptionwas thrown, the existingcatch (JsonException)recovery path (fall back to the.bkbackup, then create a fresh instance) never runs.Why it matters / concrete failure scenario
Any settings file whose content is (or has been overwritten/truncated/corrupted to) the literal text
nullpermanently breaksLoadOrCreate()for that app with an unhandled crash, instead of falling back to the backup or a fresh instance the way every other malformed-content case already does. This is a realistic corruption shape (e.g. a half-written file, or a writer that persistsnullfor a missing root) that isn't covered by the existing tests —TestLoadOrCreateHandlesCorruptFileandTestLoadOrCreateRecoversFromCorruptBackupboth use syntactically invalid JSON, which does throwJsonExceptionand is handled correctly; the valid-but-null case is untested and unhandled.Suggested fix
Treat a
nulldeserialization result the same as aJsonException, e.g.:inside the existing try block, so it flows into the current backup/fresh-instance recovery logic.
Acceptance criteria
nullresults inLoadOrCreate()recovering (via backup or a fresh instance) instead of throwing.