stride3d/stride · error · ArgumentException
Cannot add an asset with an empty Id
Error message
Cannot add an asset with an empty Id
What it means
PackageAssetCollection.CheckCanAdd validates every asset before it is inserted into a package's asset collection. Every asset must carry a non-empty AssetId because the collection indexes assets by id (mapIdToPath) and serialized references resolve through it. An asset with AssetId.Empty cannot be referenced or tracked, so the collection refuses it with this ArgumentException.
Solutions
- Assign a fresh id before adding: asset.Id = AssetId.New() (or Guid.NewGuid()).
- If the asset came from deserialization, verify the Id field is present and non-empty in the source file.
- For clone scenarios, copy the original asset's Id, or generate a new one intentionally if the clone is a separate asset.
- Wrap the Add call in a check: if (item.Id == AssetId.Empty) regenerate the id before adding.
Example fix
// before
var asset = new AssetItem(location, myAsset);
package.Assets.Add(asset); // throws: empty Id
// after
var asset = new AssetItem(location, myAsset) { Id = AssetId.New() };
package.Assets.Add(asset); Defensive patterns
Strategy: validation
Validate before calling
if (item.Id == AssetId.Empty)
item.Id = AssetId.New();
package.Assets.Add(item); Type guard
bool HasValidId(AssetItem a) => a.Id != AssetId.Empty;
Try / catch
try { package.Assets.Add(item); }
catch (ArgumentException e) when (e.Message.Contains("empty Id"))
{ item.Id = AssetId.New(); package.Assets.Add(item); } Prevention
- Always construct AssetItem via helpers that assign AssetId.New()
- Never hand-edit Id fields in serialized asset files
- After deserialization, assert ids are non-empty before inserting into collections
When it happens
Trigger: Calling Package.Assets.Add(item) (or any Add overload that routes through CheckCanAdd) with an asset whose Id property equals AssetId.Empty — e.g. a manually constructed AssetItem whose Id was never generated, or a deserialized asset that lost its id.
Common situations: Creating an AssetItem with 'new AssetItem { ... }' and forgetting to assign Id = Guid.NewGuid() (or AssetId.New()); hand-editing or round-tripping YAML that dropped the Id field; copying asset properties onto a fresh instance without copying the id.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Type [ ] must be assignable to Asset or be a Package
- Cannot add an asset that is already added to another package
- Asset location [ ] must be relative and not absolute (not…
- An asset [ ] with the same location [ ] is already…
- An asset with the same id
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/61706c0075583852.
Report an issue: GitHub.
Appendix: source
Thrown at sources/assets/Stride.Core.Assets/PackageAssetCollection.cs:325
/// or
/// Asset location [{0}] cannot start with relative '..'.ToFormat(location);item
/// </exception>
public void CheckCanAdd(AssetItem item)
{
// TODO better handle interaction
if (item == null)
{
throw new ArgumentNullException(nameof(item), "Cannot add an empty asset item reference");
}
if (registeredItems.Contains(item))
{
throw new ArgumentException("Asset already exist in this collection", nameof(item));
}
if (item.Id == AssetId.Empty)
{
throw new ArgumentException("Cannot add an asset with an empty Id", nameof(item));
}
if (item.Package != null && item.Package != Package)
{
throw new ArgumentException("Cannot add an asset that is already added to another package", nameof(item));
}
// 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)View on GitHub (pinned to 96fad776d2)