stride3d/stride · error · InvalidOperationException
importing stage-only methods/variables is only possible at…
Error message
importing stage-only methods/variables is only possible at the root mixin
What it means
Importing stage-only methods/variables (shaderClass.ImportStageOnly, SDSL `import stage`) is only legal at the root mixin — the node with no Stage. MergeClassInBuffers enforces this and throws InvalidOperationException when such an import appears in a nested/staged mixin, because stage-only symbols cannot be replicated into a non-root stage context.
Solutions
- Move the `import stage` shader to the root of the mixin tree
- Remove `import stage` (use regular import) if stage-only symbols aren't required
- Restructure composition so stage-only symbols are only referenced from the root mixin
Example fix
// before
var nested = new ShaderMixinSource { Mixins = { importStageShader } }; // used as composition of root
// after
var root = new ShaderMixinSource { Mixins = { importStageShader, otherMixin } }; // import stage at root Defensive patterns
Strategy: try-catch
Validate before calling
if (shaderClass.ImportStageOnly && mixinNode.Stage != null)
throw new InvalidOperationException("import stage shader must be at root mixin"); Try / catch
try { result = mixer.MergeSDSL(tree); }
catch (InvalidOperationException ex) when (ex.Message.Contains("importing stage-only")) {
moveShaderToRootMixin();
} Prevention
- Place `import stage` shaders only at the tree root
- Audit inheritance chains for mid-level stage imports
- Prefer plain `import` unless stage-only symbols are truly needed
When it happens
Trigger: A shader using `import stage` (or ImportStageOnly=true) is mixed at a non-root node (mixinNode.Stage != null) during MergeClassInBuffers via shaderInfo; composing an `import stage` shader into a nested composition path.
Common situations: Applying an `import stage` utility shader as a nested composition instead of the root; inheritance chains where a mid-level shader imports stage-only symbols; refactor moving an `import stage` shader deeper into the tree.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- No composition was supplied for
- Could not find cbuffer member link info for
- [Color] attribute can only be applied on float3/float4…
- Unsupported float vector size
- Unsupported int vector size
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/62bfbe8fac1ca133.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs:421
}
private static string ComposeLinkName(string linkName, string? compositionPath = null)
{
if (compositionPath != null)
linkName += $".{compositionPath}";
return linkName;
}
// Append CompositionPath to "Link" for any non-stage variable
// Also force-emit the missing "Link" decorations
private ShaderInfo MergeClassInBuffers(MixinGlobalContext globalContext, SpirvContext context, SpirvBuffer buffer, MixinNode mixinNode, ShaderClassInstantiation shaderClass, TypeDuplicateHelper typeDuplicateInserter)
{
var isRootMixin = mixinNode.Stage == null;
if (shaderClass.ImportStageOnly)
{
if (!isRootMixin)
throw new InvalidOperationException("importing stage-only methods/variables is only possible at the root mixin");
}
var shaderBuffers = shaderClass.Buffer ?? throw new InvalidOperationException($"Shader buffers not loaded for {shaderClass.ClassName}");
var offset = context.Bound;
var resourceGroupOffset = context.ResourceGroupBound;
// Remember when we started to add instructions in both context and main buffer
var shaderStart = buffer.Count;
var contextStart = context.Count;
var names = new Dictionary<int, string>();
var forbiddenIds = new HashSet<int>();
var remapIds = new Dictionary<int, int>();
var removedIds = new HashSet<int>();
bool isContext = true;
// Note: FunctionType is only required when looking for stage functionView on GitHub (pinned to 96fad776d2)