stride3d/stride · error · InvalidOperationException

One of the asset does not match the directory hierarchy.

Error message

One of the asset does not match the directory hierarchy.

What it means

When performing a clipboard/selection operation on a set of assets relative to a target directory, the editor validates that each asset's location actually lives under the expected parent path. Locations in namespaced packages are rooted at /Namespace/... while the directory tree is bare, so a mismatch means one asset does not belong to the directory hierarchy being operated on and the operation is aborted.

Solutions

  1. Verify all selected assets belong to the same package/directory subtree as the target directory before the operation.
  2. Use the asset's UnqualifiedUrl (not the namespaced FullPath) when doing your own hierarchy checks.
  3. For root-level targets, handle directories with Parent == null separately instead of passing them with non-rooted assets.

Example fix

// before
vm.CopyToClipboard(selectedAssets, targetDirectory);
// after
var expected = targetDirectory.Parent?.Path;
if (selectedAssets.All(a => a.AssetItem.UnqualifiedUrl.FullPath.StartsWith(expected, StringComparison.Ordinal)))
    vm.CopyToClipboard(selectedAssets, targetDirectory);
Defensive patterns

Strategy: validation

Validate before calling

var parentPath = directory.Parent?.Path;
bool allMatch = assets.All(a => parentPath != null && a.AssetItem.UnqualifiedUrl.FullPath.StartsWith(parentPath, StringComparison.Ordinal));
if (!allMatch) throw new InvalidOperationException("Selection spans directories/packages; split the operation");

Try / catch

try { vm.CopyToClipboard(assets, directory); }
catch (InvalidOperationException ex) { logger.Warn(ex, "Asset does not match directory hierarchy; check package/namespace"); }

Prevention

When it happens

Trigger: Copy/cut/clipboard operations (or any API passing a directory plus a set of assets) where an asset's UnqualifiedUrl path does not start with directory.Parent.Path — e.g. mixing assets from a different package or namespace, or passing the wrong directory argument.

Common situations: Editor plugins or scripts copying assets across packages/namespaces; passing a root-level directory (Parent == null) with non-rooted assets; mixing selection contents from multiple packages.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sources/editor/Stride.Core.Assets.Editor/ViewModel/AssetCollectionViewModel.cs:1069

            }

            return collection;
        }

        /// <summary>
        /// Consistency check. Makes sure <paramref name="assets"/> are indeed inside the given <paramref name="directory"/>.
        /// </summary>
        /// <param name="assets"></param>
        /// <param name="directory"></param>
        private static void EnsureDirectoryHierarchy(IEnumerable<AssetViewModel> assets, DirectoryBaseViewModel directory)
        {
            foreach (var asset in assets)
            {
                // Locations in namespaced packages are rooted /Namespace/...; the directory tree is bare
                var location = asset.AssetItem.UnqualifiedUrl;
                if (location.HasDirectory && (directory.Parent == null || !location.FullPath.StartsWith(directory.Parent.Path, StringComparison.Ordinal)))
                {
                    throw new InvalidOperationException("One of the asset does not match the directory hierarchy.");
                }
            }
        }

        /// <summary>
        /// Actually writes the assets to the clipboard.
        /// </summary>
        /// <param name="assetsToWrite"></param>
        /// <returns></returns>
        private bool WriteToClipboard(IEnumerable<IGrouping<string, AssetViewModel>> assetsToWrite)
        {
            var assetCollection = new List<AssetItem>();
            assetCollection.AddRange(assetsToWrite.SelectMany(
                    grp => grp.Select(a => new AssetItem(UPath.Combine<UFile>(grp.Key, a.AssetItem.Location.GetFileNameWithoutExtension()), a.AssetItem.Asset))));
            try
            {
                var text = ServiceProvider.TryGet<ICopyPasteService>()?.CopyMultipleAssets(assetCollection);
                if (string.IsNullOrEmpty(text))

View on GitHub (pinned to 96fad776d2)