stride3d/stride · error · ArgumentException

Asset already exist in this collection

Error message

Asset already exist in this collection

What it means

CheckCanAdd rejects an AssetItem already present in registeredItems, throwing ArgumentException "Asset already exist in this collection". The collection enforces unique asset items (by identity) so duplicate registration is a caller bug.

Solutions

  1. Check collection.Contains(item) before Add, or use a try-add pattern
  2. Remove/Clear existing items before re-adding during reloads
  3. Deduplicate scan results before registering

Example fix

// before
foreach (var item in scannedItems) package.Assets.Add(item);
// after
foreach (var item in scannedItems)
    if (!package.Assets.Contains(item)) package.Assets.Add(item);
Defensive patterns

Strategy: validation

Validate before calling

if (item is null || assets.Contains(item)) continue;

Try / catch

try { assets.Add(item); } catch (ArgumentException ex) when (ex.Message.Contains("already exist")) { log.Debug($"Duplicate asset skipped: {item.Location}"); }

Prevention

When it happens

Trigger: Calling Add twice with the same AssetItem, or adding an item that compares equal (Contains) to an existing one — e.g. re-running an import/scan that re-registers assets without clearing the collection.

Common situations: Reloading a package into an existing collection without Remove/Clear first; duplicate assets from overlapping directory scans; idempotency issues in scripts.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

    /// An asset with the same id [{0}] is already registered with the location [{1}].ToFormat(item.Id, location.Path);item
    /// or
    /// Asset location [{0}] cannot contain drive information.ToFormat(location);item
    /// or
    /// Asset location [{0}] must be relative and not absolute (not start with '/').ToFormat(location);item
    /// 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).

View on GitHub (pinned to 96fad776d2)