stride3d/stride · error · InvalidOperationException

Passthrough already started

Error message

Passthrough already started

What it means

StartPassthrough() throws this InvalidOperationException when the FB passthrough extension instance reports it is already Enabled. Each device can have only one active passthrough session; starting a second one would conflict with the first. Callers should keep the IDisposable returned by StartPassthrough and dispose it to stop passthrough before starting again.

Solutions

  1. Keep the IDisposable from StartPassthrough and dispose it before calling StartPassthrough again.
  2. Track a bool/nullable field so StartPassthrough is only invoked when no passthrough session is active.
  3. Wrap StartPassthrough in a check (or a try/catch for InvalidOperationException) to make starting idempotent.

Example fix

// before
passthrough = hmd.StartPassthrough(); // called every frame, throws once enabled
// after
passthrough ??= hmd.StartPassthrough(); // start once, dispose to stop
// or:
if (passthrough == null) passthrough = hmd.StartPassthrough();
Defensive patterns

Strategy: type-guard

Validate before calling

if (activePassthrough != null) return; // already started
currentPassthrough = hmd.StartPassthrough();

Type guard

bool PassthroughActive(IDisposable? session) => session != null;

Try / catch

try { passthrough = hmd.StartPassthrough(); }
catch (InvalidOperationException) { /* already started; reuse existing session */ }

Prevention

When it happens

Trigger: Calling StartPassthrough() twice without disposing the IDisposable returned by the first call; storing the first disposable but calling StartPassthrough again on a later frame.

Common situations: Double initialization from both a scene script and a game-loop update; re-entering a VR scene that starts passthrough each time without stopping the previous session; missing ownership tracking of the returned disposable.

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

Appendix: source

Thrown at sources/engine/Stride.VirtualReality/OpenXR/OpenXRHmd.cs:395

                Type = StructureType.InteractionProfileState,
                Next = null
            };
            Xr.StringToPath(Instance, "/user/hand/left", ref leftHandPath);
        }

        public override IDisposable StartPassthrough()
        {
            if (!passthroughRequested)
                throw new InvalidOperationException("The passthrough mode needs to be enabled at device creation");

            if (!SupportsPassthrough)
                throw new NotSupportedException("The device does not support passthrough mode");

            if (passthroughExt is null)
                passthroughExt = new OpenXRExt_FB_Passthrough(Xr, globalSession, Instance);

            if (passthroughExt.Enabled)
                throw new InvalidOperationException("Passthrough already started");

            passthroughExt.Enabled = true;

            return new AnonymousDisposable(() => passthroughExt.Enabled = false);
        }

        private void EndNullFrame()
        {
            FrameEndInfo frame_end_info = new FrameEndInfo()
            {
                Type = StructureType.FrameEndInfo,
                DisplayTime = globalFrameState.PredictedDisplayTime,
                EnvironmentBlendMode = EnvironmentBlendMode.Opaque,
                LayerCount = 0,
                Layers = null,
                Next = null
            };
            Xr.EndFrame(globalSession, frame_end_info).CheckResult();

View on GitHub (pinned to 96fad776d2)