stride3d/stride · error · InvalidOperationException

Unable to find the graph corresponding to the base part

Error message

Unable to find the graph corresponding to the base part

What it means

In AssetCompositeHierarchyPropertyGraph's base-asset change handler, this InvalidOperationException is thrown when Container.TryGetGraph(baseAsset.Id) does not return an AssetCompositeHierarchyPropertyGraph<TAssetPartDesign,TAssetPart>. A part changed in the base asset, but its graph is missing or not a hierarchy graph, so instance parts cannot be propagated. It enforces the invariant that a hierarchy base asset always has a matching hierarchy graph in the container.

Solutions

  1. Load/restore the base composite asset so its hierarchy graph is registered in the container
  2. Re-save or recreate the derived asset with a valid base reference
  3. Rebuild the asset database/package to re-register missing graphs
  4. Remove the base linkage from the derived asset if inheritance is no longer wanted

Example fix

// before
var graph = container.TryGetGraph(baseAssetId); // may be null / wrong type
// after
var graph = container.TryGetGraph(baseAssetId);
if (graph is null) { Console.Error.WriteLine($"Base asset {baseAssetId} not loaded; skipping propagation"); return; }
Defensive patterns

Strategy: try-catch

Validate before calling

var graph = Container.TryGetGraph(baseAsset.Id);
bool canPropagate = graph is AssetCompositeHierarchyPropertyGraph<TAssetPartDesign, TAssetPart>;

Type guard

if (Container.TryGetGraph(baseAsset.Id) is AssetCompositeHierarchyPropertyGraph<TAssetPartDesign, TAssetPart> baseGraph) { /* safe to propagate */ }

Try / catch

try { HandleBaseChanged(e); } catch (InvalidOperationException ex) when (ex.Message.Contains("graph corresponding to the base part")) { // reload base asset or remove derived asset's base link }

Prevention

When it happens

Trigger: Receiving a BaseAssetChanged event whose base asset Id has no graph registered in the container, or whose graph is a different graph type (e.g. a plain AssetPropertyGraph rather than a hierarchy graph).

Common situations: Base composite assets (prefab/scene-like assets) deleted or unloaded while derived assets remain; type mismatches after changing an asset's type; partial package loads; corrupted or hand-edited asset files with stale base references.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sources/assets/Stride.Core.Assets.Quantum/AssetCompositeHierarchyPropertyGraph.cs:681

            }
        }

        // Register to new base part events
        foreach (var basePartAsset in newBasePartAsset)
        {
            basePartAsset.PartAdded += PartAddedInBaseAsset;
            basePartAsset.PartRemoved += PartRemovedInBaseAsset;
        }
    }

    private void PartAddedInBaseAsset(object? sender, AssetPartChangeEventArgs e)
    {
        UpdatingPropertyFromBase = true;

        var baseAsset = (AssetCompositeHierarchy<TAssetPartDesign, TAssetPart>)e.Asset;
        var newPart = baseAsset.Hierarchy.Parts[e.PartId];
        var newPartParent = baseAsset.GetParent(newPart.Part);
        if (Container.TryGetGraph(baseAsset.Id) is not AssetCompositeHierarchyPropertyGraph<TAssetPartDesign, TAssetPart> baseAssetGraph) throw new InvalidOperationException("Unable to find the graph corresponding to the base part");

        foreach (var instanceId in basePartAssets[baseAssetGraph])
        {
            // Discard the part if this asset don't want it
            if (!ShouldAddNewPartFromBase(baseAssetGraph, newPart, newPartParent, instanceId))
                continue;

            var insertIndex = FindBestInsertIndex(baseAsset, newPart, newPartParent, instanceId, out var instanceParent);
            if (insertIndex < 0)
                continue;

            // Now we know where to insert, let's clone the new part.
            const SubHierarchyCloneFlags flags = SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects | SubHierarchyCloneFlags.RemoveOverrides;
            var baseHierarchy = CloneSubHierarchies(baseAssetGraph.Container.NodeContainer, baseAssetGraph.Asset, newPart.Part.Id.Yield(), flags, out Dictionary<Guid, Guid> mapping);
            foreach (var ids in mapping)
            {
                // Process only ids that correspond to parts
                if (!baseHierarchy.Parts.TryGetValue(ids.Value, out var clone))

View on GitHub (pinned to 96fad776d2)