Skip to content

A 'provided' inside parentheses followed by a comma throws NullReferenceException from the parser #813

Description

@Rafael-SOWNet
"(1 provided x > 0, 2)".ToEntity()    // NullReferenceException

A raw NullReferenceException escapes the parser. Every parse failure is supposed to arrive as something under AngouriMathBaseException — see Docs/Usage/Exceptions.md — so a caller that correctly catches AngouriMathBaseException around user input does not catch this one and the process goes down.

Minimised on master (21f0d16)

input result
1 provided x > 0 1 provided x > 0 ✅
(1 provided x > 0) 1 provided x > 0 ✅
(1, 2) UnhandledParseException ✅ (correct rejection)
(x, y) UnhandledParseException ✅
(1 provided x > 0, 2) NullReferenceException ❌
(a provided b, c) NullReferenceException ❌
(1 provided x > 0, 2, 3) NullReferenceException ❌

The trigger is narrow and exact: a provided inside parentheses followed by a comma. Without the comma it parses; with the comma but without the provided it is rejected cleanly.

Where it probably comes from

That shape is the piecewise syntax. #326 records the current form as

Piecewise(a provided b, c provided d) without the otherwise case
Piecewise(a provided b, c provided d, e) with the otherwise case

and neither of those parses either:

Piecewise(1 provided x > 0, 2 provided x < 0, 3)
  -> UnhandledParseException: line 1:26 no viable alternative at input '*(1providedx>0'

Note the * in that message — Piecewise( is being read as the variable Piecewise multiplied by a bracket, which is the "unknown name followed by a bracket" path that #733 was about. So the grammar appears to have a rule for the comma-list-of-provided form but no Piecewise keyword in front of it, and the rule it does have dereferences something it did not build.

Two things worth separating:

  1. The crash is a defect regardless of what the syntax should be. Whatever Syntax for Piecewise? #326 settles on, an input that is not valid must be refused with a typed exception rather than an NRE.
  2. What the syntax should be is Syntax for Piecewise? #326, which is still open and marked Opinions wanted. That the documented current form does not parse is useful information for that discussion.

Fixing 1 does not require deciding 2.

Activity

  1. Rafael-SOWNet commented on Aug 8, 2026

    @Rafael-SOWNet
    MemberAuthor

    Fixed in #824, and one claim in the report above needs withdrawing.

    The crash is wider than stated. A provided after the comma crashes too:

    (1, 2 provided x > 0)                  -> NullReferenceException
    (1 provided x > 0, 2 provided x < 0)   -> NullReferenceException
    

    So the trigger is a provided anywhere in a parenthesised comma list, not specifically one followed by a comma.

    The piecewise claim above is wrong. I wrote that neither documented form parses. Both do, and so does the third:

    piecewise(1 provided x > 0, 2)                     ->  piecewise(1 provided (x > 0), 2 provided True)
    piecewise(1 provided x > 0, 2 provided x < 0)      ->  parses
    piecewise(1 provided x > 0, 2 provided x < 0, 3)   ->  piecewise(..., 3 provided True)
    

    I tested with Piecewise( — the capitalisation used in #326's prose — and that fails as an unknown name followed by a bracket, which is #733. The lowercase keyword the grammar actually defines works. The inference I drew from it ("the grammar appears to have a rule for the comma-list-of-provided form but no Piecewise keyword in front of it") was therefore backwards: the rule and the keyword both exist. What has no rule is the comma list without a piecewise in front of it — and that is exactly what the crash was hiding.

    That also means this issue has nothing useful to contribute to #326 after all, which is worth saying since I claimed it did.

    The cause, for the record: ANTLR reports the syntax error to the listener and then recovers, continuing with a rule context whose value was never assigned; the grammar's action for provided runs anyway and dereferences it. The error was recorded the whole time — the parse just died before ParseSilent read it. The fix reads it, guarded on an error having been reported so that a genuine bug of ours still propagates rather than being laundered into a syntax error.

  2. added theissue type on Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions