ppy/osu · error

The score specifies an incompatible set of mods!

Error message

The score specifies an incompatible set of mods!

What it means

Thrown during ScoreImporter.PreImport when ModUtils.CheckCompatibleSet(model.Mods) returns false, i.e. the score's mod list contains mutually incompatible mods (e.g. conflicting difficulty reducers, duplicates, or multipliers that cancel). The importer refuses to persist a score with an impossible mod combination.

Source

Thrown at osu.Game/Scoring/ScoreImporter.cs:95

        protected override void Populate(ScoreInfo model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
        {
            Debug.Assert(model.BeatmapInfo != null);

            // Ensure the beatmap is not detached.
            if (!model.BeatmapInfo.IsManaged)
                model.BeatmapInfo = realm.Find<BeatmapInfo>(model.BeatmapInfo.ID)!;

            if (!model.Ruleset.IsManaged)
                model.Ruleset = realm.Find<RulesetInfo>(model.Ruleset.ShortName)!;

            // These properties are known to be non-null, but these final checks ensure a null hasn't come from somewhere (or the refetch has failed).
            // Under no circumstance do we want these to be written to realm as null.
            ArgumentNullException.ThrowIfNull(model.BeatmapInfo);
            ArgumentNullException.ThrowIfNull(model.Ruleset);

            if (!ModUtils.CheckCompatibleSet(model.Mods))
                throw new InvalidOperationException(@"The score specifies an incompatible set of mods!");

            if (string.IsNullOrEmpty(model.StatisticsJson))
                model.StatisticsJson = JsonConvert.SerializeObject(model.Statistics);

            if (string.IsNullOrEmpty(model.MaximumStatisticsJson))
                model.MaximumStatisticsJson = JsonConvert.SerializeObject(model.MaximumStatistics);
        }

        // Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores).

        // TODO: `UserLookupCache` cannot currently be used here because of async foibles.
        // It only supports lookups by user ID (username would require web changes), and even then the ID lookups cannot be used.
        // That is because that component provides an async interface, and async functions cannot be consumed safely here due to the rigid structure of `RealmArchiveModelImporter`.
        // The importer has two paths, one async and one sync; the async path runs the sync path in a task.
        // This means that sometimes `PostImport()` is called from a sync context, and sometimes from an async one, whilst itself being a sync method.
        // That in turn makes `.GetResultSafely()` not callable inside `PostImport()`, as it will throw when called from an async context,
        private readonly Dictionary<int, APIUser> idLookupCache = new Dictionary<int, APIUser>();
        private readonly Dictionary<string, APIUser> usernameLookupCache = new Dictionary<string, APIUser>();

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Inspect model.Mods and run ModUtils.CheckCompatibleSet locally to identify the offending pair(s).
  2. Strip the incompatible mod(s) before import, or reject the score as invalid.
  3. If a legit score fails after a ruleset update, re-evaluate the mod's IncompatibleMods/Multiplier handling.
  4. For batch imports, log and quarantine incompatible scores rather than aborting the run.

Example fix

// before
if (!ModUtils.CheckCompatibleSet(model.Mods))
    throw new InvalidOperationException(@"The score specifies an incompatible set of mods!");

// after (diagnostic)
if (!ModUtils.CheckCompatibleSet(model.Mods, out var invalidMods))
    throw new InvalidOperationException($"Incompatible mods: {string.Join(", ", invalidMods.Select(m => m.Name))}");
Defensive patterns

Strategy: validation

Validate before calling

if (!ModUtils.CheckCompatibleSet(score.Mods))
    throw new InvalidOperationException("Refusing to import score with incompatible mods.");

Type guard

static bool HasCompatibleMods(ScoreInfo s) => ModUtils.CheckCompatibleSet(s.Mods);

Try / catch

try { importer.Import(score); }
catch (InvalidOperationException ex) when (ex.Message.Contains("incompatible set of mods")) { /* quarantine */ }

Prevention

When it happens

Trigger: Importing a score whose Mods array fails ModUtils.CheckCompatibleSet: duplicate mods, mods that blacklist each other (e.g. two multipliers), or a mod marked incompatible with another present mod.

Common situations: Tampered or hand-crafted .osr/.json scores with illegal mod combos; a mod's IncompatibleMods list was broadened and old scores now fail re-import; ruleset mod changes that made previously-allowed combos invalid.

Related errors


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