ppy/osu · error · InvalidOperationException

Cannot Begin a rotate operation while another is in progress

Error message

Cannot Begin a rotate operation while another is in progress!

What it means

OsuSelectionRotationHandler.Begin() initiates a rotation operation: it snapshots original positions and control points, computes the default origin, and starts a change-handler transaction via changeHandler.BeginChange(). Calling Begin when OperationInProgress.Value is already true would overwrite the original position snapshots (losing the ability to revert) and start a nested change transaction.

Source

Thrown at osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs:56

            updateState();
        }

        private void updateState()
        {
            var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects);
            CanRotateAroundSelectionOrigin.Value = quad.Width > 0 || quad.Height > 0;
            CanRotateAroundPlayfieldOrigin.Value = selectedMovableObjects.Any();
        }

        private OsuHitObject[]? objectsInRotation;

        private Dictionary<OsuHitObject, Vector2>? originalPositions;
        private Dictionary<IHasPath, Vector2[]>? originalPathControlPointPositions;

        public override void Begin()
        {
            if (OperationInProgress.Value)
                throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!");

            base.Begin();

            changeHandler?.BeginChange();

            objectsInRotation = selectedMovableObjects.ToArray();
            DefaultOrigin = GeometryUtils.MinimumEnclosingCircle(objectsInRotation).Item1;
            originalPositions = objectsInRotation.ToDictionary(obj => obj, obj => obj.Position);
            originalPathControlPointPositions = objectsInRotation.OfType<IHasPath>().ToDictionary(
                obj => obj,
                obj => obj.Path.ControlPoints.Select(point => point.Position).ToArray());
        }

        public override void Update(float rotation, Vector2? origin = null)
        {
            if (!OperationInProgress.Value)
                throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!");

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Check OperationInProgress.Value before calling Begin(), and only begin if no operation is in progress.
  2. Ensure every Begin() has a matching Commit() in the same interaction lifecycle (e.g., mouse-down begins, mouse-up commits).
  3. Use a single input handler for rotation gestures to prevent concurrent Begin calls.

Example fix

// before
rotationHandler.Begin(); // may double-fire

// after
if (!rotationHandler.OperationInProgress.Value)
    rotationHandler.Begin();
Defensive patterns

Strategy: validation

Validate before calling

// Guard Begin against double-entry
if (rotationHandler.OperationInProgress.Value)
    return; // or log a warning
rotationHandler.Begin();

Prevention

When it happens

Trigger: Calling Begin() when OperationInProgress.Value is already true — for example, a rotation handle's mouse-down event firing twice without an intervening mouse-up/Commit, or two independent input sources (keyboard + mouse) both calling Begin.

Common situations: Double-binding of a rotation gesture handler; concurrent input from multiple sources; state machine reset that clears OperationInProgress without calling Commit on the handler.

Related errors


AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13). Data as JSON: /api/errors/76f0fd0e27ad143e. Report an issue: GitHub.