stride3d/stride · error · InvalidOperationException
This method can only be called during step
Error message
This method can only be called during step [{step}] What it means
MaterialGeneratorContext exposes methods (AddShaderSource, SetMultiplePasses, callbacks, stream modifiers) that are only valid while the material compilation is inside a specific MaterialGeneratorStep. EnsureStep compares the context's current Step with the required step and throws InvalidOperationException when they differ. This guards the pipeline against calling phase-specific APIs out of order.
Solutions
- Move the API call (AddShaderSource, SetMultiplePasses, AddFinalCallback, etc.) into the code that runs during the corresponding MaterialGeneratorStep
- If inside a generator, verify you are overriding/executing the correct step's method rather than an earlier/later one
- Check whether the context has already been advanced (e.g. Prepare called twice or reused across passes) and use a fresh context if so
Example fix
// before (in Initialize / wrong phase)
context.AddShaderSource(typeof(MyShader));
// after (inside ProcessMaterialGenerator with step == MaterialGeneratorStep.ShaderSourceGenerator)
public override void Generate(MaterialGeneratorContext context)
{
context.EnsureStep(MaterialGeneratorStep.ShaderSourceGenerator); // ensure correct phase
context.AddShaderSource(typeof(MyShader));
} Defensive patterns
Strategy: validation
Validate before calling
// ensure the context is in the expected step before calling phase-specific APIs
if (context.Step != MaterialGeneratorStep.ShaderSourceGenerator)
throw new InvalidOperationException($"AddShaderSource requires step {MaterialGeneratorStep.ShaderSourceGenerator}, current: {context.Step}"); Type guard
static bool InStep(MaterialGeneratorContext ctx, MaterialGeneratorStep step) => ctx.Step == step;
Try / catch
try { context.AddShaderSource(typeof(MyShader)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("This method can only be called during step")) {
logger.Warn("Material generator API called in wrong step; skipped");
} Prevention
- Call step-specific MaterialGeneratorContext APIs only inside the matching override of your material generator
- Never cache the context across phases or frames
- Write custom generators by copying the step order of built-in generators
When it happens
Trigger: Calling MaterialGeneratorContext.AddShaderSource/HasShaderSources outside the ShaderSourceGenerator step, SetMultiplePasses outside the MultiplePasses step, or AddFinalCallback/SetStreamFinalModifier/GetStreamFinalModifier outside the Finalize step (e.g. from a custom material pass or plugin invoked at the wrong stage).
Common situations: Writing a custom IMaterialGenerator or MaterialPass that calls stream/shader APIs in its constructor or in the wrong override; calling these APIs from a different thread or after Prepare/Build has advanced past the expected step.
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
- Begin must be called before functionName
- End must be called before functionName
- Expecting texture input on slot
- Expecting texture output on slot
- Expecting less than textures in input
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/4f9cd1d88e7593c4.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Rendering/Rendering/Materials/MaterialGeneratorContext.cs:620
}
stageContext.Streams.Clear();
// Squash all ShaderSources to a single shader source
var materialBlendLayerMixin = stageContext.ComputeShaderSource();
stageContext.ShaderSources.Clear();
// Add the shader to the mixin
shaderMixinSource.AddComposition("layer", materialBlendLayerMixin);
// Squash the shader sources
stageContext.ShaderSources.Add(shaderMixinSource);
}
}
private void EnsureStep(MaterialGeneratorStep step)
{
if (Step != step)
throw new InvalidOperationException($"This method can only be called during step [{step}]");
}
}
}
View on GitHub (pinned to 96fad776d2)