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

  1. Move the API call (AddShaderSource, SetMultiplePasses, AddFinalCallback, etc.) into the code that runs during the corresponding MaterialGeneratorStep
  2. If inside a generator, verify you are overriding/executing the correct step's method rather than an earlier/later one
  3. 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

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


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)