stride3d/stride · error · InvalidOperationException

parent cannot be null

Error message

parent cannot be null

What it means

During Undo of a FolderDeleted operation, the code tries to locate the deleted folder's parent folder; if the parent reference is null it cannot recreate the folder and throws InvalidOperationException with 'parent cannot be null'.

Solutions

  1. Ensure undo operations are cleared when the asset/hierarchy is reloaded so stale operations are not replayed
  2. Check why the parent folder no longer exists (asset externally modified) and reconcile state before undo
  3. Guard undo of FolderDeleted with a check that the parent path still resolves
  4. Recreate the missing parent folders before replaying the operation

Example fix

// before
op.Undo(); // may throw if hierarchy reloaded
// after
if (op.ResolvesParent()) op.Undo(); else assetVm.ReloadOperations();
Defensive patterns

Strategy: try-catch

Validate before calling

if (parent == null || !asset.Folders.Contains(parent)) { RefreshHierarchy(); return; }

Type guard

static bool CanUndoFolderDelete(EntityFolderOperation op) => op.ResolvesParent();

Try / catch

try { op.Undo(); }
catch (InvalidOperationException ex) { log.Warn(ex); editor.RefreshHierarchy(); }

Prevention

When it happens

Trigger: Undoing a folder deletion after the hierarchy was reloaded/changed so the resolved parent (parent?.Folders...) is null and the folder cannot be recreated under it.

Common situations: Undo stacks surviving asset reloads or scene switches; deleting a root-level folder whose parent lookup returns null; editor state desynchronized from disk after external modifications.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/63af2629628f2bcf. Report an issue: GitHub.

Appendix: source

Thrown at sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/ViewModels/EntityFolderOperation.cs:92

            {
                parent = parent?.Folders.FirstOrDefault(x => string.Equals(x.Name, folderPath[i], EntityFolderViewModel.FolderCase));
            }
            // Note: empty folders are not saved and can "disappear" after closing/reopening an editor.
            // In order for action to be undo/redo-able we must allow the possibility to not find the folder.
            EntityFolderViewModel folder;
            switch (action)
            {
                case Action.FolderCreated:
                    folder = parent?.Folders.FirstOrDefault(x => string.Equals(x.Name, folderName, EntityFolderViewModel.FolderCase));
                    folder?.Delete();
                    action = Action.FolderDeleted;
                    break;
                case Action.FolderDeleted:
                    folder = parent?.Folders.FirstOrDefault(x => string.Equals(x.Name, folderName, EntityFolderViewModel.FolderCase));
                    if (folder == null && editor != null)
                    {
                        // folder not found, let's recreate it
                        if (parent == null) throw new InvalidOperationException($"{nameof(parent)} cannot be null");
                        folder = editor.CreateFolder(asset, parent, false);
                        folder.Name = folderName;
                    }
                    action = Action.FolderCreated;
                    break;
                case Action.FolderRenamed:
                    folder = parent?.Folders.FirstOrDefault(x => string.Equals(x.Name, folderName, EntityFolderViewModel.FolderCase));
                    if (folder != null)
                    {
                        folder.Name = oldFolderName;
                    }
                    folderPath[folderPath.Length - 1] = oldFolderName;
                    oldFolderName = folderName;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

View on GitHub (pinned to 96fad776d2)