ppy/osu · error

Can't Read 1

Error message

Can't Read 1

What it means

Thrown while reading the 8-byte little-endian uncompressed-size field of an LZMA replay stream, when ReadByte() returns -1 (EOF) before all 8 bytes are read. The properties header was present but the stream is still truncated within the metadata region. Sibling of the 'input .lzma is too short' check.

Source

Thrown at osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs:182

            return score;
        }

        private void readCompressedData(byte[] data, Action<StreamReader> readFunc)
        {
            using (var replayInStream = new MemoryStream(data))
            {
                byte[] properties = new byte[5];
                if (replayInStream.Read(properties, 0, 5) != 5)
                    throw new IOException("input .lzma is too short");

                long outSize = 0;

                for (int i = 0; i < 8; i++)
                {
                    int v = replayInStream.ReadByte();
                    if (v < 0)
                        throw new IOException("Can't Read 1");

                    outSize |= (long)(byte)v << (8 * i);
                }

                long compressedSize = replayInStream.Length - replayInStream.Position;

                using (var lzma = LzmaStream.Create(properties, replayInStream, compressedSize, outSize))
                using (var reader = new StreamReader(lzma))
                    readFunc(reader);
            }
        }

        /// <summary>
        /// Populates the <see cref="ScoreInfo.MaximumStatistics"/> for a given <see cref="ScoreInfo"/>.
        /// </summary>
        /// <param name="score">The score to populate the statistics of.</param>
        /// <param name="workingBeatmap">The corresponding <see cref="WorkingBeatmap"/>.</param>
        public static void PopulateMaximumStatistics(ScoreInfo score, WorkingBeatmap workingBeatmap)

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Validate data.Length >= 13 before entering readCompressedData so the metadata region is guaranteed present.
  2. Re-acquire the replay file from its original source and retry the import.
  3. Wrap the decode in try/catch IOException and skip/quarantine corrupt replays during batch import.
  4. Audit the replay encoder to ensure it writes the full 13-byte header atomically.

Example fix

// before
int v = replayInStream.ReadByte();
if (v < 0)
    throw new IOException("Can't Read 1");

// after (precheck)
if (data.Length < 13)
    throw new IOException($"Replay metadata truncated ({data.Length} bytes); need at least 13.");
Defensive patterns

Strategy: validation

Validate before calling

if (data == null || data.Length < 13)
    throw new IOException($"Replay metadata truncated: {data?.Length ?? 0} bytes.");

Try / catch

try { readCompressedData(data, readFunc); }
catch (IOException ex) when (ex.Message.Contains("Read 1")) { /* corrupt replay */ }

Prevention

When it happens

Trigger: The replay byte array contains between 5 and 12 bytes inclusive: enough for the LZMA properties but not enough for the full 8-byte outSize length prefix.

Common situations: Partially-overwritten replay file, a writer bug that wrote the properties header then failed before the length field, or manual slicing of a compressed payload that cut mid-header.

Related errors


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