antlr/antlr4 · error · IllegalStateException
This set is readonly
Error message
This set is readonly
What it means
ATNConfigSet is sealed with readonly=true before it is stored as the configuration set of a DFA state; DFA caching assumes those configs never change. add()/addAll() are mutators and throw IllegalStateException on a sealed set.
Source
Thrown at runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java:139
public boolean add(ATNConfig config) {
return add(config, null);
}
/**
* Adding a new config means merging contexts with existing configs for
* {@code (s, i, pi, _)}, where {@code s} is the
* {@link ATNConfig#state}, {@code i} is the {@link ATNConfig#alt}, and
* {@code pi} is the {@link ATNConfig#semanticContext}. We use
* {@code (s,i,pi)} as key.
*
* <p>This method updates {@link #dipsIntoOuterContext} and
* {@link #hasSemanticContext} when necessary.</p>
*/
public boolean add(
ATNConfig config,
DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache)
{
if ( readonly ) throw new IllegalStateException("This set is readonly");
if ( config.semanticContext != SemanticContext.Empty.Instance ) {
hasSemanticContext = true;
}
if (config.getOuterContextDepth() > 0) {
dipsIntoOuterContext = true;
}
ATNConfig existing = configLookup.getOrAdd(config);
if ( existing==config ) { // we added this new one
cachedHashCode = -1;
configs.add(config); // track order here
return true;
}
// a previous (s,i,pi,_), merge with it and save result
boolean rootIsWildcard = !fullCtx;
PredictionContext merged =
PredictionContext.merge(existing.context, config.context, rootIsWildcard, mergeCache);
// no need to check for existing.context, config.context in cache
// since only way to create new graphs is "call rule" and here. WeView on GitHub (pinned to 7d5770395b)
Solutions
- Check !configs.isReadonly() before mutating.
- Create a new ATNConfigSet or use new ATNConfigSet(existingConfigSet) for modifications.
- Never mutate configuration sets reachable from DFAState objects.
- Build and optimize a set before it is installed in a DFA cache.
Example fix
// before DFAState dfaState = ...; dfaState.configs.add(newConfig); // readonly after DFA caching // after ATNConfigSet mutable = new ATNConfigSet(dfaState.configs); mutable.add(newConfig);
Defensive patterns
Strategy: validation
Validate before calling
if (configs.isReadonly()) {
configs = new ATNConfigSet(configs);
}
configs.add(newConfig); Try / catch
try {
configs.add(config);
} catch (IllegalStateException e) {
if (configs.isReadonly()) {
configs = new ATNConfigSet(configs);
configs.add(config);
} else {
throw e;
}
} Prevention
- Treat DFAState.configs as immutable.
- Copy before modifying.
- Keep scratch sets separate from cached DFA sets.
When it happens
Trigger: Calling add or addAll on DFAState.configs obtained from ParserATNSimulator/LexerATNSimulator; reusing a set after it was passed to code that calls setReadonly(true); or retaining a set and later trying to append configurations.
Common situations: Custom ATN simulators, debug tools that inspect and then modify DFA states, and code that copies references rather than creating a fresh ATNConfigSet.
Related errors
- This method is not implemented for readonly sets.
- The object is read only.
- This ATN simulator does not support clearing the DFA.
- Invalid state number.
- Could not deserialize ATN with version %d (expected %d).
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/30da8ad7c4f5dd2e.
Report an issue: GitHub.