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
- Verify all selected assets belong to the same package/directory subtree as the target directory before the operation.
- Use the asset's UnqualifiedUrl (not the namespaced FullPath) when doing your own hierarchy checks.
- 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
- Keep selections within one package and directory subtree
- Compare paths using UnqualifiedUrl, not the namespaced FullPath
- Watch for null Parent when targeting root directories
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
- Type [ ] must be assignable to Asset or be a Package
- Cannot add an asset with an empty Id
- The specified DefaultCharacter is not part of this font.
- The provided path is not a valid path name.
- Build manifest [ ] doesn't exist
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)