ppy/osu · error · InvalidOperationException

Cannot Begin a scale operation while another is in progress!

Error message

Cannot Begin a scale operation while another is in progress!

What it means

OsuSelectionScaleHandler.Begin() initiates a scale operation: it snapshots original hit-object states (via OriginalHitObjectState), computes the surrounding quad and convex hull, and starts a change-handler transaction. Calling Begin when OperationInProgress.Value is already true would overwrite the snapshots and start a nested transaction.

Source

Thrown at osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs:88

        private void updateState()
        {
            var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects);

            CanScaleX.Value = Precision.DefinitelyBigger(quad.Width, 0);
            CanScaleY.Value = Precision.DefinitelyBigger(quad.Height, 0);
            CanScaleDiagonally.Value = CanScaleX.Value && CanScaleY.Value;
            CanScaleFromPlayfieldOrigin.Value = selectedMovableObjects.Any();
            IsScalingSlider.Value = selectedMovableObjects.Count() == 1 && selectedMovableObjects.First() is Slider;
        }

        private Dictionary<OsuHitObject, OriginalHitObjectState>? objectsInScale;
        private Vector2? defaultOrigin;
        private List<Vector2>? originalConvexHull;

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

            base.Begin();

            changeHandler?.BeginChange();

            objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho));
            OriginalSurroundingQuad = GeometryUtils.GetSurroundingQuad(objectsInScale.Keys);
            originalConvexHull = GeometryUtils.GetConvexHull(objectsInScale.Keys);
            defaultOrigin = GeometryUtils.MinimumEnclosingCircle(originalConvexHull).Item1;
        }

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

            Debug.Assert(objectsInScale != null && defaultOrigin != null && OriginalSurroundingQuad != null);

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Check OperationInProgress.Value before calling Begin().
  2. Ensure every Begin() has a matching Commit() in the same interaction lifecycle.
  3. Route all scale input through a single handler to prevent concurrent Begin calls.

Example fix

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

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

Strategy: validation

Validate before calling

// Guard Begin against double-entry
if (scaleHandler.OperationInProgress.Value)
    return;
scaleHandler.Begin();

Prevention

When it happens

Trigger: Calling Begin() when OperationInProgress.Value is already true — for example, a scale handle's mouse-down event firing twice without an intervening mouse-up/Commit.

Common situations: Double-binding of a scale gesture handler; concurrent input from multiple sources; state machine that doesn't enforce single-operation discipline.

Related errors


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