{"record":{"id":"c6282ee158aaad27","repo":"ppy/osu","slug":"the-score-specifies-an-incompatible-set-of-mods","errorCode":null,"errorMessage":"The score specifies an incompatible set of mods!","messagePattern":"The score specifies an incompatible set of mods!","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"osu.Game/Scoring/ScoreImporter.cs","lineNumber":95,"sourceCode":"\r\n        protected override void Populate(ScoreInfo model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)\r\n        {\r\n            Debug.Assert(model.BeatmapInfo != null);\r\n\r\n            // Ensure the beatmap is not detached.\r\n            if (!model.BeatmapInfo.IsManaged)\r\n                model.BeatmapInfo = realm.Find<BeatmapInfo>(model.BeatmapInfo.ID)!;\r\n\r\n            if (!model.Ruleset.IsManaged)\r\n                model.Ruleset = realm.Find<RulesetInfo>(model.Ruleset.ShortName)!;\r\n\r\n            // 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).\r\n            // Under no circumstance do we want these to be written to realm as null.\r\n            ArgumentNullException.ThrowIfNull(model.BeatmapInfo);\r\n            ArgumentNullException.ThrowIfNull(model.Ruleset);\r\n\r\n            if (!ModUtils.CheckCompatibleSet(model.Mods))\r\n                throw new InvalidOperationException(@\"The score specifies an incompatible set of mods!\");\r\n\r\n            if (string.IsNullOrEmpty(model.StatisticsJson))\r\n                model.StatisticsJson = JsonConvert.SerializeObject(model.Statistics);\r\n\r\n            if (string.IsNullOrEmpty(model.MaximumStatisticsJson))\r\n                model.MaximumStatisticsJson = JsonConvert.SerializeObject(model.MaximumStatistics);\r\n        }\r\n\r\n        // Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores).\r\n\r\n        // TODO: `UserLookupCache` cannot currently be used here because of async foibles.\r\n        // It only supports lookups by user ID (username would require web changes), and even then the ID lookups cannot be used.\r\n        // That is because that component provides an async interface, and async functions cannot be consumed safely here due to the rigid structure of `RealmArchiveModelImporter`.\r\n        // The importer has two paths, one async and one sync; the async path runs the sync path in a task.\r\n        // This means that sometimes `PostImport()` is called from a sync context, and sometimes from an async one, whilst itself being a sync method.\r\n        // That in turn makes `.GetResultSafely()` not callable inside `PostImport()`, as it will throw when called from an async context,\r\n        private readonly Dictionary<int, APIUser> idLookupCache = new Dictionary<int, APIUser>();\r\n        private readonly Dictionary<string, APIUser> usernameLookupCache = new Dictionary<string, APIUser>();\r","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game/Scoring/ScoreImporter.cs#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect model.Mods and run ModUtils.CheckCompatibleSet locally to identify the offending pair(s).","Strip the incompatible mod(s) before import, or reject the score as invalid.","If a legit score fails after a ruleset update, re-evaluate the mod's IncompatibleMods/Multiplier handling.","For batch imports, log and quarantine incompatible scores rather than aborting the run."],"exampleFix":"// before\nif (!ModUtils.CheckCompatibleSet(model.Mods))\n    throw new InvalidOperationException(@\"The score specifies an incompatible set of mods!\");\n\n// after (diagnostic)\nif (!ModUtils.CheckCompatibleSet(model.Mods, out var invalidMods))\n    throw new InvalidOperationException($\"Incompatible mods: {string.Join(\", \", invalidMods.Select(m => m.Name))}\");","handlingStrategy":"validation","validationCode":"if (!ModUtils.CheckCompatibleSet(score.Mods))\n    throw new InvalidOperationException(\"Refusing to import score with incompatible mods.\");","typeGuard":"static bool HasCompatibleMods(ScoreInfo s) => ModUtils.CheckCompatibleSet(s.Mods);","tryCatchPattern":"try { importer.Import(score); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"incompatible set of mods\")) { /* quarantine */ }","preventionTips":["Run ModUtils.CheckCompatibleSet before import.","Sanitize mod lists from untrusted replay sources.","After ruleset mod changes, re-validate stored scores before re-import."],"tags":["score","mods","import","validation"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}