stride3d/stride · error · ArgumentException
Asset location [ ] must be relative and not absolute (not…
Error message
Asset location [{0}] must be relative and not absolute (not start with '/') What it means
Asset locations must be package-relative. When the container has no AssetNamespace, CheckCanAdd rejects absolute locations (those starting with '/'), because absolute paths break the package-relative addressing model used by references and mounting.
Solutions
- Strip the leading '/' and pass a relative location, e.g. 'Assets/sword'.
- If the asset lives in a namespaced package, ensure the container's AssetNamespace is set so the location gets qualified automatically.
- Normalize the path before Add: use UFile/UDirectory APIs to convert absolute to relative paths against the package root.
- Validate with !location.IsAbsolute before calling Add.
Example fix
// before
var item = new AssetItem(new UFile("/Assets/sword"), asset);
package.Assets.Add(item); // throws: absolute location
// after
var item = new AssetItem(new UFile("Assets/sword"), asset);
package.Assets.Add(item); Defensive patterns
Strategy: validation
Validate before calling
if (item.Location.IsAbsolute)
item.Location = MakeRelativeToPackage(item.Location, package);
package.Assets.Add(item); Type guard
bool IsRelativeLocation(UFile loc) => !loc.IsAbsolute;
Try / catch
try { package.Assets.Add(item); }
catch (ArgumentException e) when (e.Message.Contains("must be relative"))
{ /* normalize location to relative and retry */ } Prevention
- Store locations as package-relative virtual paths only
- Never pass absolute/vfs mount paths to Add
- Qualify through the container API when namespaces are in play
When it happens
Trigger: Calling package.Assets.Add(item) where item.Location.IsAbsolute (starts with '/') and the package/container does not define an AssetNamespace that would qualify the location. Note: when a namespace exists the location is auto-qualified instead of thrown.
Common situations: Building a location string like "/MyGame/Assets/sword" from a virtual file system path and passing it to Add; copying locations from mount paths or URLs instead of package-relative paths.
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
- Asset location [ ] cannot contain drive information
- Asset location [ ] cannot start with relative '..
- 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/3e8b0f7adb1c6392.
Report an issue: GitHub.
Appendix: source
Thrown at sources/assets/Stride.Core.Assets/PackageAssetCollection.cs:349
}
// Note: we ignore name collisions if asset is not referenceable
var referenceable = item.Asset.GetType().GetCustomAttribute<AssetDescriptionAttribute>()?.Referenceable ?? true;
// Namespaced packages root their locations /Namespace/...; creation paths author
// unqualified locations, qualified here like the loaders do. Plain packages stay
// relative-only (that reservation is what makes rooted URLs collision-free).
// Detached packages (clones, pack-time copies) have no container: locations pass through.
var location = item.Location;
if (Package.Container is { } container)
{
if (container.AssetNamespace is not null)
{
item.Location = location = container.Qualify(location);
}
else if (location.IsAbsolute)
{
throw new ArgumentException("Asset location [{0}] must be relative and not absolute (not start with '/')".ToFormat(location), nameof(item));
}
}
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));
}
View on GitHub (pinned to 96fad776d2)