stride3d/stride · error · InvalidOperationException

Unable to retrieve the game-side node

Error message

Unable to retrieve the game-side node

What it means

Inside the propagator's change-handling loop, each asset change event should carry, under GameSideContentKey, a reference to the linked game-side node established during Initialize. If GetContent(GameSideContentKey) returns null, the link was never created or was lost, and the propagator cannot apply the change to the game side, so it throws InvalidOperationException.

Solutions

  1. Re-create the GameEditorChangePropagator (re-run Initialize) so new parts get linked before their changes are processed.
  2. Catch this error in the propagator loop and skip/requeue changes for unlinked nodes instead of crashing the task.
  3. Verify the part was instanced game-side at link time; guard additions so they trigger a relink.

Example fix

// before
var gameSideNode = assetNode.GetContent(GameSideContentKey);
if (gameSideNode == null)
    throw new InvalidOperationException("Unable to retrieve the game-side node");
// after
var gameSideNode = assetNode.GetContent(GameSideContentKey);
if (gameSideNode == null)
{
    logger.Warning($"No game-side node linked for {e.Node}; relinking");
    await RelinkAsync(assetNode);
    continue;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (assetNode.GetContent(GameSideContentKey) == null)
    await RelinkAsync(assetNode); // ensure link before processing changes

Try / catch

try { HandleChange(e); }
catch (InvalidOperationException ex) when (ex.Message.Contains("game-side node")) { queueRelink(e.Node); }

Prevention

When it happens

Trigger: A change event fires for an asset node that was never linked (created after Initialize failed silently, or its containing part had no game-side instance), then PropagatorTask processes the queued change.

Common situations: Adding new entities/parts to an asset while the game preview is running before the propagator re-linked; a previous 'Unable to reach the game-side instance' error left nodes unlinked but the propagator kept running; custom asset types not supported by the game-side node container.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at sources/editor/Stride.Assets.Presentation/AssetEditors/GameEditor/ViewModels/GameEditorChangePropagator.cs:168

                        // Ok to await inside the loop, this call should never yield because we already own the lock.
                        await Editor.Controller.Loader.Manager.PushContentReference(Owner.Id, contentReference.ContentId, contentReference.Node, contentReference.Index);
                    }
                }
            });

            initialized.SetResult(0);

            while (running)
            {
                var e = await propagatorQueue.ReceiveAsync();
                // This allow to post null when disposing so we evaluate running again to exit this task
                if (e == null)
                    continue;

                var assetNode = (IAssetNode)e.Node;
                var gameSideNode = assetNode.GetContent(GameSideContentKey);
                if (gameSideNode == null)
                    throw new InvalidOperationException("Unable to retrieve the game-side node");

                var index = (e as ItemChangeEventArgs)?.Index ?? NodeIndex.Empty;
                var innerType = e.Node.Descriptor.GetInnerCollectionType();
                if (!AssetRegistry.CanBeAssignedToContentTypes(innerType, checkIsUrlType: false))
                {
                    if (Editor.NodeContainer.NodeBuilder.PrimitiveTypeFilter.IsPrimitiveType(innerType))
                    {
                        // No need to retrieve and/or duplicate the value for value type, we just propagate the change
                        await Editor.Controller.InvokeAsync(() => UpdateGameSideContent(gameSideNode, e.NewValue, e.ChangeType, index));
                    }
                    else
                    {
                        var value = RetrieveValue(assetNode, e.NewValue, e.ChangeType, index);

                        if (value == null && assetNode.Descriptor.Category == DescriptorCategory.Set)
                        {
                            value = e.NewValue;
                        }

View on GitHub (pinned to 96fad776d2)