stride3d/stride · error · InvalidOperationException

"videoTexture" was not deallocated properly before trying…

Error message

"videoTexture" was not deallocated properly before trying to create a new one!

What it means

VideoInstance.AllocateVideoTexture throws this InvalidOperationException when a video texture already exists and has not been released. The class enforces a strict allocate/deallocate lifecycle: only one VideoTexture may be live at a time, so re-allocation without an intervening DeallocateVideoTexture is treated as a caller bug rather than being silently tolerated.

Solutions

  1. Call DeallocateVideoTexture (or the public API that triggers it, e.g. stopping/invalidating the video) before calling AllocateVideoTexture again.
  2. Ensure the playback lifecycle is linear: allocate -> play -> deallocate -> (optionally) allocate again.
  3. If reusing the instance, reset it (seek to start / re-prepare via the media player) so its internal texture is released first.
  4. Wrap the sequence in try/finally so the texture is deallocated even when playback throws.

Example fix

// before
videoInstance.AllocateVideoTexture(1920, 1080);
videoInstance.Play();
videoInstance.AllocateVideoTexture(1920, 1080); // throws

// after
videoInstance.AllocateVideoTexture(1920, 1080);
videoInstance.Play();
videoInstance.DeallocateVideoTexture();
videoInstance.AllocateVideoTexture(1920, 1080); // ok
Defensive patterns

Strategy: try-catch

Validate before calling

// C#
bool canAllocate = videoInstance != null; // texture state is internal; wrap allocation

Try / catch

// C#
try { videoInstance.AllocateVideoTexture(w, h); }
catch (InvalidOperationException ex) when (ex.Message.Contains("not deallocated"))
{
    // deallocate and retry once
}

Prevention

When it happens

Trigger: Calling AllocateVideoTexture(width, height) when the internal videoTexture field is non-null — i.e. after a previous allocation that was not followed by DeallocateVideoTexture, or re-preparing/seeking/restarting the same VideoInstance without releasing its texture.

Common situations: Replaying or restarting a video without disposing the previous playback; calling video preparation APIs twice in a row; a playback path that skips the deallocation step on error or early exit; reusing a cached VideoInstance across plays.

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

Appendix: source

Thrown at sources/engine/Stride.Video/VideoInstance.cs:428

        {
            if (url == null || startPosition < 0 || length < 0)
                return;

            var factory = videoSystem.ActiveBackendFactory
                ?? throw new InvalidOperationException("No video backend is registered or supported on this platform.");
            backend = factory.CreateBackend(this);
            mediaInitialized = backend.Initialize(url, startPosition, length);
            if (!mediaInitialized)
            {
                backend.Dispose();
                backend = null;
            }
        }

        internal void AllocateVideoTexture(int width, int height)
        {
            if (videoTexture != null)
                throw new InvalidOperationException("\"videoTexture\" was not deallocated properly before trying to create a new one!");

            videoTexture = new VideoTexture(GraphicsDevice, services, width, height, videoComponent.MaxMipMapCount);
        }

        private void DeallocateVideoTexture()
        {
            videoTexture?.Dispose();
            videoTexture = null;
        }

        // Backend-accessible state. Internal to keep VideoInstance's public surface stable.
        internal IServiceRegistry Services => services;
        internal VideoComponent VideoComponent => videoComponent;
        internal VideoTexture VideoTexture => videoTexture;
        internal void SetCurrentTime(TimeSpan time) => CurrentTime = time;
        internal void SetDuration(TimeSpan duration) => Duration = duration;

        // Called by a backend right after it writes a freshly-decoded frame to the target.

View on GitHub (pinned to 96fad776d2)