ppy/osu · error · InvalidOperationException
Cannot Commit a rotate operation without calling Begin first
Error message
Cannot Commit a rotate operation without calling Begin first!
What it means
OsuSelectionRotationHandler.Commit() ends the change-handler transaction (changeHandler.EndChange()) and nulls the rotation state. Without a preceding Begin(), there is no open transaction to end and no state to clean up. The guard prevents calling EndChange on an unopened transaction.
Source
Thrown at osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs:96
foreach (var ho in objectsInRotation)
{
ho.Position = GeometryUtils.RotatePointAroundOrigin(originalPositions[ho], actualOrigin, rotation);
if (ho is IHasPath withPath)
{
var originalPath = originalPathControlPointPositions[withPath];
for (int i = 0; i < withPath.Path.ControlPoints.Count; ++i)
withPath.Path.ControlPoints[i].Position = GeometryUtils.RotatePointAroundOrigin(originalPath[i], Vector2.Zero, rotation);
}
}
}
public override void Commit()
{
if (!OperationInProgress.Value)
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
changeHandler?.EndChange();
base.Commit();
objectsInRotation = null;
originalPositions = null;
originalPathControlPointPositions = null;
DefaultOrigin = null;
}
private IEnumerable<OsuHitObject> selectedMovableObjects => selectedItems.Cast<OsuHitObject>()
.Where(h => h is not Spinner);
}
}
View on GitHub (pinned to d9c73e12ad)
Solutions
- Ensure Begin() is called before Commit().
- Guard Commit() behind an OperationInProgress.Value check to skip if no operation is active.
Example fix
// before
rotationHandler.Commit(); // no prior Begin
// after
if (rotationHandler.OperationInProgress.Value)
rotationHandler.Commit(); Defensive patterns
Strategy: validation
Validate before calling
// Guard Commit against calls without a prior Begin
if (rotationHandler.OperationInProgress.Value)
rotationHandler.Commit(); Prevention
- Pair Begin and Commit in a using/try-finally pattern to guarantee lifecycle correctness.
- Guard Commit behind OperationInProgress.Value to make it idempotent.
When it happens
Trigger: Calling Commit() when OperationInProgress.Value is false — e.g., a mouse-up/commit event firing without a prior Begin, or the operation was already committed and Commit fires again.
Common situations: Double-commit from duplicate event handlers; state machine lifecycle mismatch between the UI control and the rotation handler.
Related errors
- Cannot Begin a rotate operation while another is in progress
- Cannot Update 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/274cc68bb3bec187.
Report an issue: GitHub.