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
- Relocate the asset so its location is inside the package, and pass a location without leading '..' segments.
- If the asset genuinely belongs to shared content, put it in the package that actually roots it and reference it from there.
- Normalize the path (resolve '..' against the package root) and recompute a clean relative location.
- 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
- Resolve '..' segments before creating AssetItem
- Keep assets physically inside their package folder
- Use the container's path APIs instead of manual string joins
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
- Asset location [ ] must be relative and not absolute (not…
- Asset location [ ] cannot contain drive information
- An IObjectNode was expected when processing the path
- An IMemberNode was expected when processing the path
- Cannot add an asset with an empty Id
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)