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
- Re-create the GameEditorChangePropagator (re-run Initialize) so new parts get linked before their changes are processed.
- Catch this error in the propagator loop and skip/requeue changes for unlinked nodes instead of crashing the task.
- 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
- Re-run propagator Initialize after adding parts to a live asset
- Treat missing GameSideContentKey links as 'needs relink' in custom propagators
- Log and skip unlinked nodes instead of failing the whole propagator task
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)