prevent Date objects from being silently dropped during state persist#21677
Merged
prevent Date objects from being silently dropped during state persist#21677
Conversation
Aragas
previously approved these changes
Mar 19, 2026
|
This PR has conflicts. You need to rebase the PR before it can be merged. |
…ence
Date objects (e.g. installTime) were treated as plain objects by the state
diff engine because isObject() only checked for non-null, typeof "object",
and non-array. Since Date has no enumerable own keys, Object.keys() returned
[], causing collectSetOperations and collectRemoveOperations to generate no
diff operations for Date values.
This meant:
- When a mod was installed, installTime (set as new Date()) was never
persisted to LevelDB
- When a mod was removed, the old installTime string (from the initial
startInstallCB) was never removed from LevelDB
- On next startup, the orphaned installTime reconstructed a partial mod
entry: { attributes: { installTime: "..." } } with no installationPath
- The state verifier detected the missing required field and deleted the
entry with deleteBroken: "parent", potentially disrupting the mod state
Fix: isObject() now checks Object.getPrototypeOf(state) === Object.prototype
to ensure only plain objects are recursed into. Date and other built-in
objects are treated as leaf values and correctly generate set/remove ops.
Also added type: "object" with deleteBroken: true at the game-level verifier
in modsReducer to contain blast radius — a corrupt entry can only take itself
down, never the entire persistent.mods tree.
fixes https://linear.app/nexus-mods/issue/APP-73/app-not-restarting-properly
This allows the user to decide whether to sanitize, quit or ignore invalid application state before hydration. Previously we would just quit the app automatically when a state corruption is detected. closes https://linear.app/nexus-mods/issue/APP-107/re-introduce-application-state-corrupted-dialog
|
This PR doesn't have conflicts anymore. It can be merged after all status checks have passed and it has been reviewed. |
Aragas
approved these changes
Mar 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Date objects (e.g. installTime) were treated as plain objects by the state diff engine because isObject() only checked for non-null, typeof "object", and non-array. Since Date has no enumerable own keys, Object.keys() returned [], causing collectSetOperations and collectRemoveOperations to generate no diff operations for Date values.
This meant:
Fix: isObject() now checks Object.getPrototypeOf(state) === Object.prototype to ensure only plain objects are recursed into. Date and other built-in objects are treated as leaf values and correctly generate set/remove ops.
Also added type: "object" with deleteBroken: true at the game-level verifier in modsReducer to contain blast radius — a corrupt entry can only take itself down, never the entire persistent.mods tree.
Finally, re-introduced the "Application State Corrupted" dialog so the user can sanitize, quit or ignore state corruption rather than just automatically quit the app (leaving the user unable to restart Vortex)
fixes https://linear.app/nexus-mods/issue/APP-73/app-not-restarting-properly
closes https://linear.app/nexus-mods/issue/APP-107/re-introduce-application-state-corrupted-dialog