stride3d/stride · error · ArgumentException

Asset location [ ] cannot start with relative '..

Error message

Asset location [{0}] cannot start with relative '..'

What it means

Asset locations are rooted at the package root; a directory component starting with '..' would escape that root. CheckCanAdd rejects such locations because escaped locations break the package-relative addressing model and can collide with or leak into sibling packages.

Solutions

  1. Relocate the asset so its location is inside the package, and pass a location without leading '..' segments.
  2. If the asset genuinely belongs to shared content, put it in the package that actually roots it and reference it from there.
  3. Normalize the path (resolve '..' against the package root) and recompute a clean relative location.
  4. Validate with !(location.GetDirectory()?.StartsWith("..") == true) before Add.

Example fix

// before
var item = new AssetItem(new UFile("../Shared/sword"), asset);
package.Assets.Add(item); // throws: starts with '..'

// after
var item = new AssetItem(new UFile("Shared/sword"), asset);
package.Assets.Add(item);
Defensive patterns

Strategy: validation

Validate before calling

if (item.Location.GetDirectory()?.StartsWith("..") == true)
    item.Location = NormalizeInsidePackage(item.Location, package);
package.Assets.Add(item);

Type guard

bool IsInsidePackage(UFile loc) => loc.GetDirectory()?.StartsWith("..", StringComparison.Ordinal) != true;

Try / catch

try { package.Assets.Add(item); }
catch (ArgumentException e) when (e.Message.Contains("relative '..'"))
{ /* normalize the location or move the asset */ }

Prevention

When it happens

Trigger: Calling package.Assets.Add(item) where the location's directory begins with '..' (e.g. '../Shared/asset') and the container has no AssetNamespace that would re-root it.

Common situations: Computing relative paths with '..' segments when the asset file sits outside the current package folder; string-joining paths with Parent/.. navigation; moving an asset to a location that points outside the package.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/PackageAssetCollection.cs:370

        if (referenceable && mapPathToId.ContainsKey(location))
        {
            throw new ArgumentException("An asset [{0}] with the same location [{1}] is already registered ".ToFormat(mapPathToId[location], location.GetDirectoryAndFileName()), nameof(item));
        }

        if (mapIdToPath.ContainsKey(item.Id))
        {
            throw new ArgumentException("An asset with the same id [{0}] is already registered with the location [{1}]".ToFormat(item.Id, location.GetDirectoryAndFileName()), nameof(item));
        }

        if (location.HasDrive)
        {
            throw new ArgumentException("Asset location [{0}] cannot contain drive information".ToFormat(location), nameof(item));
        }

        if (location.GetDirectory()?.StartsWith("..", StringComparison.Ordinal) == true)
        {
            throw new ArgumentException("Asset location [{0}] cannot start with relative '..'".ToFormat(location), nameof(item));
        }

        // Double check that this asset is not already stored in another package for this session
        if (Package.Session != null)
        {
            foreach (var otherPackage in Package.Session.Packages)
            {
                if (otherPackage != Package)
                {
                    if (otherPackage.Assets.ContainsById(item.Id))
                    {
                        throw new ArgumentException("Cannot add the asset [{0}] [{1}] to package [{2}] [{3}]: it is already in package [{4}] [{5}] in the current session".ToFormat(item.Id, item.Location, Package.Meta.Name, Package.FullPath, otherPackage.Meta.Name, otherPackage.FullPath));
                    }
                }
            }
        }
    }

View on GitHub (pinned to 96fad776d2)