ppy/osu · error · IOException

Unknown file format (no content)

Error message

Unknown file format (no content)

What it means

Thrown by Decoder.GetDecoder when the input stream is empty or contains only whitespace/blank lines. The method peeks and consumes lines looking for the first non-empty content to identify the format magic; if PeekLine returns null (end of stream) before any content is found, the file is treated as having no content.

Source

Thrown at osu.Game/Beatmaps/Formats/Decoder.cs:73

            where T : new()
        {
            ArgumentNullException.ThrowIfNull(stream);

            if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
                throw new IOException(@"Unknown decoder type");

            // start off with the first line of the file
            string? line = stream.PeekLine()?.Trim();

            while (line != null && line.Length == 0)
            {
                // consume the previously peeked empty line and advance to the next one
                stream.ReadLine();
                line = stream.PeekLine()?.Trim();
            }

            if (line == null)
                throw new IOException("Unknown file format (no content)");

            var decoder = typedDecoders.Where(d => line.StartsWith(d.Key, StringComparison.InvariantCulture)).Select(d => d.Value).FirstOrDefault();

            // it's important the magic does NOT get consumed here, since sometimes it's part of the structure
            // (see JsonBeatmapDecoder - the magic string is the opening brace)
            // decoder implementations should therefore not die on receiving their own magic
            if (decoder != null)
                return (Decoder<T>)decoder.Invoke(line);

            if (!fallback_decoders.TryGetValue(typeof(T), out var fallbackDecoder))
                throw new IOException($"Unknown file format ({line})");

            return (Decoder<T>)fallbackDecoder.Invoke();
        }

        /// <summary>
        /// Registers an instantiation function for a <see cref="Decoder"/>.
        /// </summary>

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Check that the stream/file has a non-zero length before passing it to GetDecoder.
  2. If the file is on disk, verify File.Exists and new FileInfo(path).Length > 0 before decoding.
  3. If the stream was previously read, reset stream.Position = 0 before decoding.
  4. Re-download or re-extract the beatmap if the file is truncated.

Example fix

// before
using var reader = new LineBufferedReader(stream);
var decoder = Decoder.GetDecoder<Beatmap>(reader); // throws if stream is empty

// after
if (stream.Length == 0)
    throw new InvalidDataException("Cannot decode an empty beatmap stream");
using var reader = new LineBufferedReader(stream);
var decoder = Decoder.GetDecoder<Beatmap>(reader);
Defensive patterns

Strategy: validation

Validate before calling

// Check stream length before decoding
if (stream.Length == 0)
    throw new InvalidDataException("Stream is empty — cannot decode");
using var reader = new LineBufferedReader(stream);
var decoder = Decoder.GetDecoder<Beatmap>(reader);

Try / catch

try
{
    decoder = Decoder.GetDecoder<Beatmap>(reader);
}
catch (IOException ex) when (ex.Message.Contains("no content"))
{
    Logger.Log("Beatmap file is empty or truncated", LoggingTarget.Database);
}

Prevention

When it happens

Trigger: Calling Decoder.GetDecoder<T>(stream) on a LineBufferedReader whose underlying stream is zero-length, or contains only empty lines. This commonly happens when a beatmap or storyboard .osu/.osb file is empty or truncated to zero bytes.

Common situations: A partially-downloaded beatmap where the .osu file is empty; a file that was created but never written to; a stream whose Position was left at the end after a previous read; importing a corrupt archive where a beatmap file decompresses to zero bytes.

Related errors


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