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
- Check OperationInProgress.Value before calling Begin(), and only begin if no operation is in progress.
- Ensure every Begin() has a matching Commit() in the same interaction lifecycle (e.g., mouse-down begins, mouse-up commits).
- 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
- Always check OperationInProgress.Value before calling Begin() in input handlers.
- Design input handlers so that Begin maps to a single discrete event (e.g., mouse-down) and Commit to its counterpart (mouse-up).
- Add integration tests that simulate rapid double-clicks or concurrent input to verify no double-Begin.
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
- Cannot Update a rotate operation without calling Begin first
- Cannot Commit a rotate operation without calling Begin first
- Cannot Begin a scale operation while another is in progress!
- Cannot Update a scale operation without calling Begin first!
- Cannot Commit a rotate operation without calling Begin first
AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13).
Data as JSON: /api/errors/76f0fd0e27ad143e.
Report an issue: GitHub.