{"record":{"id":"612598a91e09eb12","repo":"ppy/osu","slug":"ruleset-with-id-of-rulesetid-not-found-locally","errorCode":null,"errorMessage":"Ruleset with ID of {RulesetID} not found locally","messagePattern":"Ruleset with ID of (.+?) not found locally","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs","lineNumber":186,"sourceCode":"        #endregion\r\n\r\n        /// <summary>\r\n        /// Whether this <see cref=\"ScoreInfo\"/> represents a legacy (osu!stable) score.\r\n        /// </summary>\r\n        [JsonIgnore]\r\n        public bool IsLegacyScore => LegacyScoreId != null;\r\n\r\n        public override string ToString() => $\"score_id: {ID} user_id: {UserID}\";\r\n\r\n        /// <summary>\r\n        /// Create a <see cref=\"ScoreInfo\"/> from an API score instance.\r\n        /// </summary>\r\n        /// <param name=\"rulesets\">A ruleset store, used to populate a ruleset instance in the returned score.</param>\r\n        /// <param name=\"beatmap\">An optional beatmap, copied into the returned score (for cases where the API does not populate the beatmap).</param>\r\n        /// <returns></returns>\r\n        public ScoreInfo ToScoreInfo(RulesetStore rulesets, BeatmapInfo? beatmap = null)\r\n        {\r\n            var ruleset = rulesets.GetRuleset(RulesetID) ?? throw new InvalidOperationException($\"Ruleset with ID of {RulesetID} not found locally\");\r\n\r\n            var rulesetInstance = ruleset.CreateInstance();\r\n\r\n            var mods = Mods.Select(apiMod => apiMod.ToMod(rulesetInstance)).ToArray();\r\n\r\n            var scoreInfo = ToScoreInfo(mods, beatmap);\r\n            scoreInfo.Ruleset = ruleset;\r\n\r\n            return scoreInfo;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Create a <see cref=\"ScoreInfo\"/> from an API score instance.\r\n        /// </summary>\r\n        /// <param name=\"mods\">The mod instances, resolved from a ruleset.</param>\r\n        /// <param name=\"beatmap\">The object to populate the scores' beatmap with.\r\n        ///<list type=\"bullet\">\r\n        /// <item>If this is a <see cref=\"BeatmapInfo\"/> type, then the score will be fully populated with the given object.</item>\r","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/ppy/osu/blob/d9c73e12adff2feaae4a3e158d36fe5883faf6ca/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs#L168-L204","documentation":"Thrown by SoloScoreInfo.ToScoreInfo when converting an API score into a local ScoreInfo. The method asks the RulesetStore for the score's RulesetID and throws InvalidOperationException if the store has no matching ruleset. The store only contains ruleset assemblies that are installed and enabled on this client.","triggerScenarios":"Calling ToScoreInfo(rulesets, beatmap) on a SoloScoreInfo whose RulesetID is not returned by rulesets.GetRuleset(RulesetID). Happens for scores originating from a ruleset the client does not have loaded (e.g. a custom ruleset DLL, or a newer ruleset than the client version).","commonSituations":"Client is missing the ruleset DLL the server used (version skew after an osu! update, disabled/removed ruleset, or a modded ruleset). Attempting to display/import a score from a ruleset the user uninstalled.","solutions":["Verify all required ruleset assemblies are present and enabled on the client (check RulesetStore.AvailableRulesets).","Guard before calling: if (!rulesets.AvailableRulesets.Any(r => r.OnlineID == score.RulesetID)) skip or filter the score.","Wrap the call site in try/catch(InvalidOperationException) and drop the unresolvable score from the list rather than crashing the import/display flow."],"exampleFix":"// before\nvar info = score.ToScoreInfo(rulesets, beatmap);\n\n// after\nvar ruleset = rulesets.GetRuleset(score.RulesetID);\nif (ruleset == null) continue; // unknown ruleset for this client\nvar info = score.ToScoreInfo(rulesets, beatmap);","handlingStrategy":"validation","validationCode":"var ruleset = rulesets.GetRuleset(score.RulesetID);\nif (ruleset == null)\n{\n    // unknown ruleset for this client; skip this score\n    continue;\n}","typeGuard":"// nullable ruleset check before conversion\nRulesetInfo? ResolveRuleset(RulesetStore store, SoloScoreInfo score)\n    => store.GetRuleset(score.RulesetID);","tryCatchPattern":"try { var info = score.ToScoreInfo(rulesets, beatmap); }\ncatch (InvalidOperationException) { /* filter score: ruleset unavailable locally */ }","preventionTips":["Never assume the server's ruleset set equals the client's; always resolve via RulesetStore first.","Filter score lists by AvailableRulesets before bulk conversion.","Log the unresolved RulesetID to telemetry so missing rulesets are detectable."],"tags":["ruleset","score","api-conversion","invalid-operation"],"backgroundTag":null,"analyzedSha":"d9c73e12adff2feaae4a3e158d36fe5883faf6ca","analyzedAt":"2026-08-13T14:12:54.015Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}