stride3d/stride · error · InvalidOperationException

Can'to detach camera

Error message

Can'to detach camera [{camera.Entity.Name}] from the graphics compositor. Another camera, {slot.Camera.Entity.Name}, is attached to this slot.

What it means

DetachCameraFromSlot throws InvalidOperationException when the compositor slot matching the camera's slot id is occupied by a different camera, so the requested camera cannot be the one detached from that slot.

Solutions

  1. Ensure the Slot.Id being detached still points at the slot this camera actually occupies
  2. Detach before changing a camera's Slot.Id
  3. Reset camera.Slot.AttachedCompositor = null when it is known to be stale, then attach cleanly
  4. Keep one camera per slot and avoid mutating Slot.Id on attached cameras

Example fix

// before
camera.Slot.Id = otherId; // slot bookkeeping now inconsistent
Detach(camera);
// after
Detach(camera);           // detach first
camera.Slot.Id = otherId;
Defensive patterns

Strategy: validation

Validate before calling

var slot = camera.Slot.AttachedCompositor?.Cameras.FirstOrDefault(s => s.Id == camera.Slot.Id);
if (slot == null || slot.Camera != camera) return; // wrong camera in slot

Try / catch

try { DetachCameraFromSlot(camera); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Another camera")) { /* re-sync slot bookkeeping */ }

Prevention

When it happens

Trigger: The camera's Slot.Id equals an occupied slot, but slot.Camera points to another camera component — e.g. after swapping Slot.Id values between two cameras, or attaching camera B to a slot then trying to detach camera A whose slot id matches.

Common situations: Swapping or reusing camera slot IDs at runtime; copying slot settings between camera components; a new camera attached to the slot after the old one's attachment bookkeeping was lost.

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

Appendix: source

Thrown at sources/engine/Stride.Engine/Engine/Processors/CameraProcessor.cs:160

                    slot.Camera = camera;
                    camera.Slot.AttachedCompositor = graphicsCompositor;
                    break;
                }
            }
        }

        private static void DetachCameraFromSlot(CameraComponent camera)
        {
            if (camera.Slot.AttachedCompositor == null)
                throw new InvalidOperationException($"The camera [{camera.Entity.Name}] isn't attached");

            for (var i = 0; i < camera.Slot.AttachedCompositor.Cameras.Count; ++i)
            {
                var slot = camera.Slot.AttachedCompositor.Cameras[i];
                if (slot.Id == camera.Slot.Id)
                {
                    if (slot.Camera != camera)
                        throw new InvalidOperationException($"Can'to detach camera [{camera.Entity.Name}] from the graphics compositor. Another camera, {slot.Camera.Entity.Name}, is attached to this slot.");

                    slot.Camera = null;
                    break;
                }
            }
            camera.Slot.AttachedCompositor = null;
        }

        private static void DetachCameraFromAllSlots(CameraComponent camera, GraphicsCompositor graphicsCompositor)
        {
            for (var i = 0; i < graphicsCompositor.Cameras.Count; ++i)
            {
                var slot = graphicsCompositor.Cameras[i];
                if (slot.Camera == camera)
                {
                    slot.Camera = null;
                }
            }

View on GitHub (pinned to 96fad776d2)