{"record":{"id":"8d47474e00143726","repo":"ppy/osu","slug":"cannot-begin-a-scale-operation-while-another-is-in","errorCode":null,"errorMessage":"Cannot Begin a scale operation while another is in progress!","messagePattern":"Cannot Begin a scale operation while another is in progress!","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs","lineNumber":88,"sourceCode":"        private void updateState()\r\n        {\r\n            var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects);\r\n\r\n            CanScaleX.Value = Precision.DefinitelyBigger(quad.Width, 0);\r\n            CanScaleY.Value = Precision.DefinitelyBigger(quad.Height, 0);\r\n            CanScaleDiagonally.Value = CanScaleX.Value && CanScaleY.Value;\r\n            CanScaleFromPlayfieldOrigin.Value = selectedMovableObjects.Any();\r\n            IsScalingSlider.Value = selectedMovableObjects.Count() == 1 && selectedMovableObjects.First() is Slider;\r\n        }\r\n\r\n        private Dictionary<OsuHitObject, OriginalHitObjectState>? objectsInScale;\r\n        private Vector2? defaultOrigin;\r\n        private List<Vector2>? originalConvexHull;\r\n\r\n        public override void Begin()\r\n        {\r\n            if (OperationInProgress.Value)\r\n                throw new InvalidOperationException($\"Cannot {nameof(Begin)} a scale operation while another is in progress!\");\r\n\r\n            base.Begin();\r\n\r\n            changeHandler?.BeginChange();\r\n\r\n            objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho));\r\n            OriginalSurroundingQuad = GeometryUtils.GetSurroundingQuad(objectsInScale.Keys);\r\n            originalConvexHull = GeometryUtils.GetConvexHull(objectsInScale.Keys);\r\n            defaultOrigin = GeometryUtils.MinimumEnclosingCircle(originalConvexHull).Item1;\r\n        }\r\n\r\n        public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both, float axisRotation = 0)\r\n        {\r\n            if (!OperationInProgress.Value)\r\n                throw new InvalidOperationException($\"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!\");\r\n\r\n            Debug.Assert(objectsInScale != null && defaultOrigin != null && OriginalSurroundingQuad != null);\r\n\r","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"Double-binding of a scale gesture handler; concurrent input from multiple sources; state machine that doesn't enforce single-operation discipline.","solutions":["Check OperationInProgress.Value before calling Begin().","Ensure every Begin() has a matching Commit() in the same interaction lifecycle.","Route all scale input through a single handler to prevent concurrent Begin calls."],"exampleFix":"// before\nscaleHandler.Begin(); // may double-fire\n\n// after\nif (!scaleHandler.OperationInProgress.Value)\n    scaleHandler.Begin();","handlingStrategy":"validation","validationCode":"// Guard Begin against double-entry\nif (scaleHandler.OperationInProgress.Value)\n    return;\nscaleHandler.Begin();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always check OperationInProgress.Value before calling Begin().","Map Begin to a single discrete input event and Commit to its counterpart.","Add tests for concurrent input scenarios."],"tags":["editor","osu-ruleset","selection","state-machine","scaling"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}