{"record":{"id":"69e83ec54c547f90","repo":"stride3d/stride","slug":"unable-to-retrieve-the-game-side-node","errorCode":null,"errorMessage":"Unable to retrieve the game-side node","messagePattern":"Unable to retrieve the game-side node","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/editor/Stride.Assets.Presentation/AssetEditors/GameEditor/ViewModels/GameEditorChangePropagator.cs","lineNumber":168,"sourceCode":"                        // Ok to await inside the loop, this call should never yield because we already own the lock.\n                        await Editor.Controller.Loader.Manager.PushContentReference(Owner.Id, contentReference.ContentId, contentReference.Node, contentReference.Index);\n                    }\n                }\n            });\n\n            initialized.SetResult(0);\n\n            while (running)\n            {\n                var e = await propagatorQueue.ReceiveAsync();\n                // This allow to post null when disposing so we evaluate running again to exit this task\n                if (e == null)\n                    continue;\n\n                var assetNode = (IAssetNode)e.Node;\n                var gameSideNode = assetNode.GetContent(GameSideContentKey);\n                if (gameSideNode == null)\n                    throw new InvalidOperationException(\"Unable to retrieve the game-side node\");\n\n                var index = (e as ItemChangeEventArgs)?.Index ?? NodeIndex.Empty;\n                var innerType = e.Node.Descriptor.GetInnerCollectionType();\n                if (!AssetRegistry.CanBeAssignedToContentTypes(innerType, checkIsUrlType: false))\n                {\n                    if (Editor.NodeContainer.NodeBuilder.PrimitiveTypeFilter.IsPrimitiveType(innerType))\n                    {\n                        // No need to retrieve and/or duplicate the value for value type, we just propagate the change\n                        await Editor.Controller.InvokeAsync(() => UpdateGameSideContent(gameSideNode, e.NewValue, e.ChangeType, index));\n                    }\n                    else\n                    {\n                        var value = RetrieveValue(assetNode, e.NewValue, e.ChangeType, index);\n\n                        if (value == null && assetNode.Descriptor.Category == DescriptorCategory.Set)\n                        {\n                            value = e.NewValue;\n                        }","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/editor/Stride.Assets.Presentation/AssetEditors/GameEditor/ViewModels/GameEditorChangePropagator.cs#L150-L186","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar gameSideNode = assetNode.GetContent(GameSideContentKey);\nif (gameSideNode == null)\n    throw new InvalidOperationException(\"Unable to retrieve the game-side node\");\n// after\nvar gameSideNode = assetNode.GetContent(GameSideContentKey);\nif (gameSideNode == null)\n{\n    logger.Warning($\"No game-side node linked for {e.Node}; relinking\");\n    await RelinkAsync(assetNode);\n    continue;\n}","handlingStrategy":"try-catch","validationCode":"if (assetNode.GetContent(GameSideContentKey) == null)\n    await RelinkAsync(assetNode); // ensure link before processing changes","typeGuard":null,"tryCatchPattern":"try { HandleChange(e); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"game-side node\")) { queueRelink(e.Node); }","preventionTips":["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"],"tags":["game-side","node-linking","synchronization","propagation"],"backgroundTag":"resource-not-found","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}