Skip to content

Tempo.TryParse("NaNbpm@1/4") throws instead of returning false, and zero/negative/infinite beats are accepted, so Seconds() returns Infinity or negative time #305

Closed

Description

@matt-edmondson

What's wrong

Semantics.Music/Tempo.cs has two validation gaps.

  1. TryParse can throw.

    • double.TryParse(..., NumberStyles.Float, ...) accepts "NaN".
    • The guard at Tempo.cs:87-89 is only bpm <= 0.0, which is false for NaN, so the value reaches Create.
    • Create (Tempo.cs:37) does check IsNaN, and throws ArgumentOutOfRangeException.

    That breaks the Try-pattern contract: anyone parsing untrusted text gets an exception.

  2. Values that make Seconds meaningless are accepted.

    • "Infinitybpm@1/4" parses.
    • Duration.TryParse accepts 0/4 and -1/4.
    • Neither Tempo.TryParse nor Tempo.Create(double, Duration) checks that the beat is positive.

    Seconds(Duration) divides by Beat.AsWholeNotes, so these values produce Infinity or negative time.

Reproduced

In a scratch net10.0 app:

Call Result
Tempo.TryParse("NaNbpm@1/4", out _) throws ArgumentOutOfRangeException
Tempo.TryParse("Infinitybpm@1/4", out t) true; t.Seconds(Quarter) = 0
Tempo.TryParse("120bpm@0/4", out t) true; Seconds(Quarter) = Infinity
Tempo.TryParse("120bpm@-1/4", out t) true; Seconds(Quarter) = −0.5
Tempo.Create(120, Duration.Create(0, 1)) succeeds; Seconds(Quarter) = Infinity

Suggested fix / acceptance criteria

  • Create throws ArgumentOutOfRangeException when beatsPerMinute is not finite (IsNaN || IsInfinity on netstandard2.0) or when the beat's Numerator <= 0.
  • TryParse returns false for the same inputs itself rather than relying on Create, so it never throws for any string.
  • Tests: every row above returns false from TryParse, and Create throws for the invalid argument cases. "120bpm@1/4" still round-trips.

Activity

  1. matt-edmondson commented on Sep 28, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: Medium. TryParse throws on untrusted input ("NaNbpm@1/4"), which breaks the Try-pattern contract. It also accepts zero, negative and infinite values that produce Infinity or negative Seconds.
    • Area / suggested assignee: Semantics.Music/Tempo.cs, and possibly Duration validation. @matt-edmondson.
    • Duplicates: None found.
    • In progress: No open PR covers it.
    • Notes: This is a small validation fix, and the issue already gives a test table. Check Duration.TryParse accepting 0/4 and -1/4 separately: other Duration consumers may have the same exposure.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions