ppy/osu · error · InvalidOperationException
Total score conversion operation returned invalid total of {
Error message
Total score conversion operation returned invalid total of {convertedTotalScoreWithoutMods} What it means
Thrown by the private convertFromLegacyTotalScore overload after the switch on ruleset legacy ID computes convertedTotalScoreWithoutMods. If the rounded result is negative, the math produced an invalid (negative) score and migration aborts rather than persisting a corrupt value. Sits just before the mod multiplier is applied.
Source
Thrown at osu.Game/Database/StandardisedScoreMigrationTools.cs:347
convertedTotalScoreWithoutMods = (long)Math.Round(
comboPortion * estimateComboProportionForCatch(attributes.MaxCombo, score.MaxCombo, score.Statistics.GetValueOrDefault(HitResult.Miss))
+ dropletsPortion * dropletsHit
+ bonusProportion);
break;
case 3:
convertedTotalScoreWithoutMods = (long)Math.Round(
150000 * comboProportion
+ 850000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy)
+ bonusProportion);
break;
default:
return (score.TotalScoreWithoutMods, score.TotalScore);
}
if (convertedTotalScoreWithoutMods < 0)
throw new InvalidOperationException($"Total score conversion operation returned invalid total of {convertedTotalScoreWithoutMods}");
long convertedTotalScore = (long)Math.Round(convertedTotalScoreWithoutMods * modMultiplier);
return (convertedTotalScoreWithoutMods, convertedTotalScore);
}
/// <summary>
/// <para>
/// For catch, the general method of calculating the combo proportion used for other rulesets is generally useless.
/// This is because in stable score V1, catch has quadratic score progression,
/// while in stable score V2, score progression is logarithmic up to 200 combo and then linear.
/// </para>
/// <para>
/// This means that applying the naive rescale method to scores with lots of short combos (think 10x 100-long combos on a 1000-object map)
/// by linearly rescaling the combo portion as given by score V1 leads to horribly underestimating it.
/// Therefore this method attempts to counteract this by calculating the best case estimate for the combo proportion that takes all of the above into account.
/// </para>
/// <para>
/// The general idea is that aside from the <paramref name="scoreMaxCombo"/> which the player is known to have hit,
View on GitHub (pinned to d9c73e12ad)
Solutions
- Inspect the failing score's inputs (comboProportion, score.Accuracy, bonusProportion) for out-of-range or NaN values; treat such scores as non-migratable.
- Clamp intermediate values to valid ranges (accuracy in [0,1], proportions in [0,1]) before the formula, or skip the score.
- If triggered by a specific ruleset's formula, fix the branch so it cannot go negative and add unit tests covering the edge inputs.
Example fix
// before
if (convertedTotalScoreWithoutMods < 0)
throw new InvalidOperationException($"Total score conversion operation returned invalid total of {convertedTotalScoreWithoutMods}");
// after (clamp + skip corrupt scores)
convertedTotalScoreWithoutMods = Math.Max(0, convertedTotalScoreWithoutMods);
if (!double.IsFinite(score.Accuracy) || score.Accuracy < 0 || score.Accuracy > 1)
{
LogForModel(score, "Score accuracy out of range; skipping conversion.");
return (score.TotalScoreWithoutMods, score.TotalScore);
} Defensive patterns
Strategy: validation
Validate before calling
if (!double.IsFinite(score.Accuracy) || score.Accuracy < 0 || score.Accuracy > 1
|| double.IsNaN(comboProportion) || double.IsNaN(bonusProportion))
{ /* skip corrupt score, keep legacy totals */ return; } Type guard
static bool IsValidScoreInput(ScoreInfo s)
=> double.IsFinite(s.Accuracy) && s.Accuracy >= 0 && s.Accuracy <= 1
&& s.MaxCombo >= 0; Try / catch
try { convertFromLegacyTotalScore(score, ruleset, difficulty, attributes); }
catch (InvalidOperationException ex) when (ex.Message.Contains("invalid total"))
{ /* quarantine score, log inputs for diagnosis */ } Prevention
- Clamp proportion inputs to [0,1] and accuracy to [0,1] before applying formulas.
- Add unit tests for each ruleset's conversion branch with extreme/edge inputs.
- Treat any non-finite intermediate as non-migratable.
When it happens
Trigger: A legacy score whose comboProportion / accuracy / bonusProportion inputs produce a negative sum after rounding — e.g. score.Accuracy or MaxCombo values that are out of expected range, or an arithmetic edge case in the per-ruleset formula branches (cases 1-3).
Common situations: Bulk score migration over old/corrupt score data; scores with anomalous statistics (negative counts, NaN accuracy); a newly added ruleset formula branch with a sign error exposed by real data.
Related errors
- Beatmap contains no hit objects!
- Cannot recalculate score multiplier as TotalScoreWithoutMods
- Error when exporting {model.GetDisplayString()}: Multiple fi
- Beatmap file {b.File!.Filename} could not be opened!
- Bad ticks count read!
AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13).
Data as JSON: /api/errors/601f697ea61c46c2.
Report an issue: GitHub.