ppy/osu · error

Only scores in the osu, taiko, catch, or mania rulesets can

Error message

Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.

What it means

Thrown by the LegacyScoreEncoder constructor when score.ScoreInfo.Ruleset.IsLegacyRuleset() is false. The legacy score format only supports the four original osu! rulesets; encoding for any other (e.g. lazer-only or custom) ruleset is rejected at construction time.

Source

Thrown at osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs:94

        private readonly Score score;
        private readonly IBeatmap? beatmap;

        /// <summary>
        /// Create a new score encoder for a specific score.
        /// </summary>
        /// <param name="score">The score to be encoded.</param>
        /// <param name="beatmap">The beatmap used to convert frames for the score. May be null if the frames are already <see cref="LegacyReplayFrame"/>s.</param>
        /// <exception cref="ArgumentException"></exception>
        public LegacyScoreEncoder(Score score, IBeatmap? beatmap)
        {
            this.score = score;
            this.beatmap = beatmap;

            if (beatmap == null && !score.Replay.Frames.All(f => f is LegacyReplayFrame))
                throw new ArgumentException(@"Beatmap must be provided if frames are not already legacy frames.", nameof(beatmap));

            if (!score.ScoreInfo.Ruleset.IsLegacyRuleset())
                throw new ArgumentException(@"Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score));
        }

        public void Encode(Stream stream, bool leaveOpen = false)
        {
            using (SerializationWriter sw = new SerializationWriter(stream, leaveOpen))
            {
                sw.Write((byte)(score.ScoreInfo.Ruleset.OnlineID));
                sw.Write(score.ScoreInfo.TotalScoreVersion);
                sw.Write(score.ScoreInfo.BeatmapInfo!.MD5Hash);
                sw.Write(score.ScoreInfo.User.Username);
                sw.Write(FormattableString.Invariant($"lazer-{score.ScoreInfo.User.Username}-{score.ScoreInfo.Date}").ComputeMD5Hash());
                sw.Write((ushort)(score.ScoreInfo.GetCount300() ?? 0));
                sw.Write((ushort)(score.ScoreInfo.GetCount100() ?? 0));
                sw.Write((ushort)(score.ScoreInfo.GetCount50() ?? 0));
                sw.Write((ushort)(score.ScoreInfo.GetCountGeki() ?? 0));
                sw.Write((ushort)(score.ScoreInfo.GetCountKatu() ?? 0));
                sw.Write((ushort)(score.ScoreInfo.GetCountMiss() ?? 0));
                sw.Write((int)(score.ScoreInfo.TotalScore));

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Filter to score.ScoreInfo.Ruleset.IsLegacyRuleset() before instantiating LegacyScoreEncoder.
  2. Use the lazer JSON score format (.osr-lazer) for non-legacy rulesets instead of the legacy encoder.
  3. Check OnlineID of the ruleset (0,1,2,3) as a quick pre-guard before constructing the encoder.
  4. Wrap construction in try/catch ArgumentException and skip non-encodable scores in batch exports.

Example fix

// before
var encoder = new LegacyScoreEncoder(score, beatmap);

// after
if (!score.ScoreInfo.Ruleset.IsLegacyRuleset())
    throw new NotSupportedException($"Ruleset {score.ScoreInfo.Ruleset.ShortName} cannot be legacy-encoded.");
var encoder = new LegacyScoreEncoder(score, beatmap);
Defensive patterns

Strategy: validation

Validate before calling

if (!score.ScoreInfo.Ruleset.IsLegacyRuleset())
    throw new NotSupportedException("Use lazer score format for non-legacy rulesets.");

Type guard

static bool IsLegacyEncodable(Score s) => s.ScoreInfo.Ruleset.IsLegacyRuleset();

Try / catch

try { new LegacyScoreEncoder(score, beatmap); }
catch (ArgumentException) { /* fall back to JSON encoder */ }

Prevention

When it happens

Trigger: Constructing new LegacyScoreEncoder(score, beatmap) where score.ScoreInfo.Ruleset is not osu/taiko/catch/mania. The encoder cannot represent non-legacy rulesets in the stable binary format.

Common situations: Exporting a score from a custom ruleset; exporting a lazer-only gamemode; forgetting to filter scores by IsLegacyRuleset() before export.

Related errors


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