{"record":{"id":"b8e744d879782593","repo":"stride3d/stride","slug":"cannot-add-an-asset-that-is-already-added-to-another-package","errorCode":null,"errorMessage":"Cannot add an asset that is already added to another package","messagePattern":"Cannot add an asset that is already added to another package","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/assets/Stride.Core.Assets/PackageAssetCollection.cs","lineNumber":330,"sourceCode":"        // TODO better handle interaction\n        if (item == null)\n        {\n            throw new ArgumentNullException(nameof(item), \"Cannot add an empty asset item reference\");\n        }\n\n        if (registeredItems.Contains(item))\n        {\n            throw new ArgumentException(\"Asset already exist in this collection\", nameof(item));\n        }\n\n        if (item.Id == AssetId.Empty)\n        {\n            throw new ArgumentException(\"Cannot add an asset with an empty Id\", nameof(item));\n        }\n\n        if (item.Package != null && item.Package != Package)\n        {\n            throw new ArgumentException(\"Cannot add an asset that is already added to another package\", nameof(item));\n        }\n\n        // Note: we ignore name collisions if asset is not referenceable\n        var referenceable = item.Asset.GetType().GetCustomAttribute<AssetDescriptionAttribute>()?.Referenceable ?? true;\n\n        // Namespaced packages root their locations /Namespace/...; creation paths author\n        // unqualified locations, qualified here like the loaders do. Plain packages stay\n        // relative-only (that reservation is what makes rooted URLs collision-free).\n        // Detached packages (clones, pack-time copies) have no container: locations pass through.\n        var location = item.Location;\n        if (Package.Container is { } container)\n        {\n            if (container.AssetNamespace is not null)\n            {\n                item.Location = location = container.Qualify(location);\n            }\n            else if (location.IsAbsolute)\n            {","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/assets/Stride.Core.Assets/PackageAssetCollection.cs#L312-L348","documentation":"An AssetItem can only belong to one package at a time; its Package property is the owning back-reference. CheckCanAdd rejects an asset whose Package is already set to a different package, because adding it here would corrupt the single-owner invariant and the session-wide id uniqueness checks.","triggerScenarios":"Calling package.Assets.Add(item) where item.Package != null and item.Package != package — e.g. adding an asset loaded from another package file, or moving an asset between packages without detaching it first.","commonSituations":"Reusing one AssetItem object across multiple Package instances in a tool; merging two packages while sharing asset instances; copying assets between packages by reference instead of by deep clone.","solutions":["Remove the asset from its current package first (currentPackage.Assets.Remove(item)), which clears ownership, then add it to the new package.","If you need the asset in both packages, deep-clone it (e.g. via AssetItem/Asset cloning with a new AssetId) instead of sharing the instance.","If this is a move operation, use the session/package APIs designed for moves rather than raw Add on two collections.","Check item.Package before adding and handle the already-owned case explicitly."],"exampleFix":"// before\nnewPackage.Assets.Add(assetItem); // throws: asset belongs to oldPackage\n\n// after\noldPackage.Assets.Remove(assetItem);\nnewPackage.Assets.Add(assetItem);","handlingStrategy":"validation","validationCode":"if (item.Package != null && item.Package != package)\n    item.Package.Assets.Remove(item);\npackage.Assets.Add(item);","typeGuard":"bool IsUnownedOrOwnedBy(AssetItem a, Package p) => a.Package == null || a.Package == p;","tryCatchPattern":"try { package.Assets.Add(item); }\ncatch (ArgumentException e) when (e.Message.Contains(\"another package\"))\n{ /* detach and retry, or deep-clone with a new id */ }","preventionTips":["Treat AssetItem as owned by exactly one package","Clone (deep) when sharing assets across packages","Use Remove before re-adding in move operations"],"tags":["assets","ownership","argument-exception"],"backgroundTag":"invalid-argument-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}