antlr/antlr4 · error · IllegalStateException
can't alter readonly IntervalSet
Error message
can't alter readonly IntervalSet
What it means
IntervalSet guards mutation with a readonly flag: once setReadonly(true) is called, clear(), add(int), add(int,int) etc. throw IllegalStateException('can't alter readonly IntervalSet'). The runtime marks shared, immutable sets (notably sets produced during ATN deserialization and grammar-derived label sets) readonly to prevent accidental corruption of shared structures.
Source
Thrown at runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java:83
}
/** Create a set with a single element, el. */
public static IntervalSet of(int a) {
IntervalSet s = new IntervalSet();
s.add(a);
return s;
}
/** Create a set with all ints within range [a..b] (inclusive) */
public static IntervalSet of(int a, int b) {
IntervalSet s = new IntervalSet();
s.add(a,b);
return s;
}
public void clear() {
if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet");
intervals.clear();
}
/** Add a single element to the set. An isolated element is stored
* as a range el..el.
*/
@Override
public void add(int el) {
if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet");
add(el,el);
}
/** Add interval; i.e., add all integers from a to b to set.
* If b<a, do nothing.
* Keep list in sorted order (by left range value).
* If overlap, combine ranges. For example,
* If this is {1..5, 10..20}, adding 6..7 yields
* {1..5, 6..7, 10..20}. Adding 4..8 yields {1..8, 10..20}.View on GitHub (pinned to 7d5770395b)
Solutions
- Clone before mutating: IntervalSet copy = new IntervalSet(readonlySet); copy.add(...); (the copy starts writable)
- If you own the set, only call setReadonly(true) after all construction is done
- For 'union with existing' logic, always build a fresh writable IntervalSet and addAll from the readonly source
Example fix
// before IntervalSet labels = transition.label(); // readonly set from ATN labels.add(extraToken); // IllegalStateException // after IntervalSet labels = new IntervalSet(transition.label()); // writable copy labels.add(extraToken);
Defensive patterns
Strategy: validation
Validate before calling
static IntervalSet writable(IntervalSet s) {
return s.isReadonly() ? new IntervalSet(s) : s;
}
// then: writable(labels).add(tokenType); Type guard
static boolean isMutable(IntervalSet s) { return !s.isReadonly(); } Try / catch
catch (IllegalStateException e) { if (e.getMessage().contains("readonly")) { IntervalSet copy = new IntervalSet(original); copy.add(x); } else throw e; } Prevention
- Treat every IntervalSet from ATN transitions as immutable
- Clone with new IntervalSet(source) before extending
- Call setReadonly(true) last, only on sets you own
When it happens
Trigger: Calling add/clear/remove on an IntervalSet obtained from runtime internals: transition.labelSet (SetTransition/NotSetTransition), ATNState or rule definitions from a deserialized ATN, or any set the ANTLR tool marked readonly. Also copying such a set into your own structure and then trying to mutate it without cloning.
Common situations: Building a custom error strategy or token filter that extends a label set from the ATN. Reusing IntervalSet.of(...) results vs. readonly ATN sets interchangeably. Tools that aggregate 'all tokens accepted here' by adding into an ATN-supplied set.
Related errors
- This set is readonly
- This method is not implemented for readonly sets.
- The object is read only.
- replace op boundaries of ${rop} overlap with previous ${prev
- insert op ${iop} within boundaries of previous ${rop}
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/de990c8eb66e68ed.
Report an issue: GitHub.