stride3d/stride · error · InvalidOperationException

Expecting texture output on slot

Error message

Expecting texture output on slot [{0}]

What it means

GetSafeOutput fetches the output render target via GetOutput and throws InvalidOperationException when the result is null, i.e. no output was ever bound (SetOutput never called, or slot beyond bound views). It guarantees a non-null texture for the caller.

Solutions

  1. Call SetOutput with the destination render target before drawing/reading
  2. Ensure the effect's Draw executed before consuming outputs
  3. Verify index is within the number of outputs passed to SetOutput(params ...)

Example fix

// before
var outTex = effect.GetSafeOutput(0); // no output bound
// after
effect.SetOutput(myRenderTarget);
// ... draw ...
var outTex = effect.GetSafeOutput(0);
Defensive patterns

Strategy: validation

Validate before calling

if (!outputBound) throw new InvalidOperationException("Call SetOutput before reading effect output");

Type guard

bool HasOutput(ImageEffect e, int i) => i == 0 ? e.GetOutput(0) != null : e.GetOutput(i) != null;

Try / catch

try { var tex = effect.GetSafeOutput(index); } catch (InvalidOperationException) { /* bind output before draw */ }

Prevention

When it happens

Trigger: Calling effect.GetSafeOutput(index) before any SetOutput/SetOutput(params) call, or with an index exceeding the number of bound outputs (outputRenderTargetViews[i] is null or array access issues).

Common situations: Reading an effect's result before Draw ran; a code path skipped SetOutput during render-target resize; mixing single-output (outputRenderTargetView) and multi-output configurations.

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/6126aced27e1e584. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Rendering/Rendering/Images/ImageEffect.cs:413

            {
                throw new ArgumentOutOfRangeException("index", string.Format("Invald texture outputindex [{0}] cannot be negative for effect [{1}]", index, Name));
            }

            return outputRenderTargetView ?? (outputRenderTargetViews != null ? outputRenderTargetViews[index] : null);
        }

        /// <summary>
        /// Gets an non-null output render target for the specified index.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns>RenderTarget.</returns>
        /// <exception cref="System.InvalidOperationException"></exception>
        protected Texture GetSafeOutput(int index)
        {
            var output = GetOutput(index);
            if (output == null)
            {
                throw new InvalidOperationException(string.Format("Expecting texture output on slot [{0}]", index));
            }

            return output;
        }

        /// <summary>
        /// Gets a render target with the specified description, scoped for the duration of the <see cref="RendererBase.DrawCore"/>.
        /// </summary>
        /// <returns>A new instance of texture.</returns>
        protected Texture NewScopedRenderTarget2D(TextureDescription description)
        {
            // TODO: Check if we should introduce an enum for the kind of scope (per DrawCore, per Frame...etc.)
            CheckIsInDrawCore();
            return PushScopedResource(Context.Allocator.GetTemporaryTexture2D(description));
        }

        /// <summary>
        /// Gets a render target output for the specified description with a single mipmap, scoped for the duration of the <see cref="RendererBase.DrawCore"/>.

View on GitHub (pinned to 96fad776d2)