antlr/antlr4 · error · InvalidOperationException

can't alter readonly IntervalSet

Error message

can't alter readonly IntervalSet

What it means

IntervalSet instances that the runtime publishes as shared constants are marked read-only; mutating them would corrupt every grammar and recognizer that shares the same set (e.g. the universal 'all tokens' or 'no tokens' sets). Clear() checks the read-only flag and throws InvalidOperationException instead of letting a shared constant be wiped.

Source

Thrown at runtime/CSharp/src/Misc/IntervalSet.cs:96

        {
            Antlr4.Runtime.Misc.IntervalSet s = new Antlr4.Runtime.Misc.IntervalSet();
            s.Add(a);
            return s;
        }

        /// <summary>Create a set with all ints within range [a..b] (inclusive)</summary>
        public static Antlr4.Runtime.Misc.IntervalSet Of(int a, int b)
        {
            Antlr4.Runtime.Misc.IntervalSet s = new Antlr4.Runtime.Misc.IntervalSet();
            s.Add(a, b);
            return s;
        }

        public virtual void Clear()
        {
            if (@readonly)
            {
                throw new InvalidOperationException("can't alter readonly IntervalSet");
            }
            intervals.Clear();
        }

        /// <summary>Add a single element to the set.</summary>
        /// <remarks>
        /// Add a single element to the set.  An isolated element is stored
        /// as a range el..el.
        /// </remarks>
        public virtual void Add(int el)
        {
            if (@readonly)
            {
                throw new InvalidOperationException("can't alter readonly IntervalSet");
            }
            Add(el, el);
        }

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Never mutate sets you did not construct; create your own set and copy: var mine = new IntervalSet(theirs)
  2. If you need to modify a set, clone it first with the IntervalSet copy constructor, which yields a writable set
  3. Keep your mutable sets locally scoped and treat all runtime-returned IntervalSets as immutable

Example fix

// before
IntervalSet expected = ctx.exception.GetExpectedTokens();
expected.Clear(); // may throw: read-only shared set

// after
IntervalSet expected = new IntervalSet(ctx.exception.GetExpectedTokens()); // writable copy
expected.Clear();
Defensive patterns

Strategy: validation

Validate before calling

// Only clear sets you constructed; clone runtime-provided sets first
IntervalSet writable = runtimeSet.IsReadOnly ? new IntervalSet(runtimeSet) : runtimeSet;
writable.Clear();

Prevention

When it happens

Trigger: Calling Clear() on an IntervalSet obtained from runtime-owned state — typically error-recovery sets like recognizer.ErrorListener/DefaultErrorStrategy.GetErrorRecoverySet, ctx.exception.getExpectedTokens(), or transitions' label sets — rather than on a set you constructed yourself.

Common situations: Custom error strategies that want to reuse and clear an expected-tokens set; copying ATN structures and mutating the source's label sets; tests that mutate parser-time interval sets to simulate grammars.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/88b95dd366752bd1. Report an issue: GitHub.