stride3d/stride · error · InvalidOperationException

Begin must be called before functionName

Error message

Begin must be called before functionName

What it means

BatchBase (the base class of SpriteBatch and UI batch renderers) tracks whether Begin() has been called via the isBeginCalled flag. Methods that draw or flush the batch (End, Draw) call CheckBeginHasBeenCalled and throw if the batch lifecycle was not started. This guards against rendering with an unset effect, viewport, or sampler state that only Begin() configures.

Solutions

  1. Ensure Begin(...) is called on the batch immediately before any Draw/End calls for that batch instance.
  2. Check that a previous exception did not abort between Begin and Draw; wrap draw code so state flags stay consistent.
  3. Verify you are calling Begin/End on the same batch instance, not two different batch objects.
  4. Do not call End twice; restructure so each Begin is paired with exactly one End.

Example fix

// before
spriteBatch.End();
// after
spriteBatch.Begin(spriteSortMode, blendState);
spriteBatch.Draw(texture, position);
spriteBatch.End();
Defensive patterns

Strategy: try-catch

Validate before calling

if (!batchStarted) throw new InvalidOperationException("Call Begin before Draw/End");
// track it yourself:
bool batchStarted = false;

Type guard

bool CanDraw(BatchBase b) => b != null && batchStarted;

Try / catch

try { spriteBatch.Draw(...); spriteBatch.End(); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Begin must be called"))
{
    log.Warn("SpriteBatch used without Begin");
}

Prevention

When it happens

Trigger: Calling batch.End(), batch.Draw(...) or batch.Flush without a preceding successful batch.Begin(...); calling End twice after Begin already consumed; an exception thrown inside Begin that skipped its final flag set while the caller continues to Draw/End.

Common situations: Copy-pasted render loops where the Begin call was deleted or commented out; early returns between Begin and End causing a second End on a stale batch object; reusing a batch across frames where the Begin call is in a branch that did not execute (e.g. empty draw list skip logic).

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/2f84b3951688bfbf. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Graphics/BatchBase.cs:271

            mutablePipeline.Update();

            // Bind pipeline
            if (mutablePipeline.State.DepthStencilState.StencilEnable)
                GraphicsContext.CommandList.SetStencilReference(stencilReferenceValue);
            GraphicsContext.CommandList.SetPipelineState(mutablePipeline.CurrentState);

            // Bind VB/IB
            if (ResourceContext.VertexBuffer != null)
                GraphicsContext.CommandList.SetVertexBuffer(0, ResourceContext.VertexBuffer, 0, vertexStructSize);
            if (ResourceContext.IndexBuffer != null)
                GraphicsContext.CommandList.SetIndexBuffer(ResourceContext.IndexBuffer, 0, indexStructSize == sizeof(int));
        }

        protected void CheckBeginHasBeenCalled(string functionName)
        {
            if (!isBeginCalled)
            {
                throw new InvalidOperationException("Begin must be called before " + functionName);
            }
        }

        protected void CheckEndHasBeenCalled(string functionName)
        {
            if (isBeginCalled)
            {
                throw new InvalidOperationException("End must be called before " + functionName);
            }
        }

        /// <summary>
        /// Flushes the sprite batch and restores the device state to how it was before Begin was called.
        /// </summary>
        public void End()
        {
            CheckBeginHasBeenCalled("End");

View on GitHub (pinned to 96fad776d2)