ppy/osu · error · InvalidDataException
Beat length cannot be NaN in a timing control point
Error message
Beat length cannot be NaN in a timing control point
What it means
Thrown by LegacyBeatmapDecoder when parsing a timing control point (TimingChange=1) whose beatLength field is NaN. The decoder explicitly rejects NaN timing points because they would corrupt the beatmap's timing grid. The value becomes NaN when the .osu file literally contains 'NaN' in the beatLength field, since double.Parse('NaN', InvariantCulture) successfully parses to double.NaN.
Source
Thrown at osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs:533
bool kiaiMode = false;
bool omitFirstBarSignature = false;
if (split.Length >= 8)
{
LegacyEffectFlags effectFlags = (LegacyEffectFlags)Parsing.ParseInt(split[7]);
kiaiMode = effectFlags.HasFlag(LegacyEffectFlags.Kiai);
omitFirstBarSignature = effectFlags.HasFlag(LegacyEffectFlags.OmitFirstBarLine);
}
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
if (stringSampleSet == @"none")
stringSampleSet = HitSampleInfo.BANK_NORMAL;
if (timingChange)
{
if (double.IsNaN(beatLength))
throw new InvalidDataException("Beat length cannot be NaN in a timing control point");
var controlPoint = CreateTimingControlPoint();
controlPoint.BeatLength = beatLength;
controlPoint.TimeSignature = timeSignature;
controlPoint.OmitFirstBarLine = omitFirstBarSignature;
addControlPoint(time, controlPoint, true);
}
int onlineRulesetID = beatmap.BeatmapInfo.Ruleset.OnlineID;
addControlPoint(time, new DifficultyControlPoint
{
GenerateTicks = !double.IsNaN(beatLength),
SliderVelocity = speedMultiplier,
}, timingChange);
View on GitHub (pinned to d9c73e12ad)
Solutions
- Open the .osu file in a text editor, find the [TimingPoints] section, and locate the offending line (the one with 'NaN' in the beatLength position).
- Replace 'NaN' with a valid positive millisecond value (e.g. 60000/BPM — for 120 BPM use 500).
- Re-export or re-download the beatmap if the corruption is widespread.
- If producing .osu files programmatically, ensure beatLength is always a valid finite double before writing.
Example fix
// before (.osu file [TimingPoints] section): // 1000,NaN,4,1,0,100,1,0 // after: // 1000,500,4,1,0,100,1,0
Defensive patterns
Strategy: validation
Validate before calling
// Validate beatLength before adding the timing point
if (timingChange && double.IsNaN(beatLength))
throw new InvalidDataException($"Timing point at {time}ms has NaN beatLength — fix the .osu file"); Try / catch
try
{
var beatmap = decoder.Decode(stream);
}
catch (InvalidDataException ex) when (ex.Message.Contains("Beat length cannot be NaN"))
{
Logger.Log("Beatmap has a NaN timing point — file is corrupt", LoggingTarget.Database);
} Prevention
- If generating .osu files programmatically, ensure beatLength is always finite before writing.
- Validate .osu timing point lines for 'NaN' before decoding.
- Re-download corrupt beatmaps rather than attempting to force-decode them.
When it happens
Trigger: Decoding a .osu file whose [TimingPoints] section contains a timing point (red/uninherited) with 'NaN' as the beatLength value — e.g. a line like '1000,NaN,4,1,0,100,1,0'. The timingChange flag must be true (first value after time is a positive beatLength, not an inherited multiplier).
Common situations: A beatmap produced by a buggy editor that wrote 'NaN' instead of a numeric BPM value; manual editing of a .osu file where a timing point value was deleted or replaced with text; a corrupt beatmap where the beatLength field is garbled.
Related errors
- Color specified in incorrect format (should be R,G,B or R,G,
- Color must be specified with 8-bit integer components
- Not a number
- Ruleset is not available locally.
- Unknown event type: {split[0]}
AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13).
Data as JSON: /api/errors/5124d0a30e4593c8.
Report an issue: GitHub.