stanfordnlp/CoreNLP · error · IllegalStateException
[INTERNAL ERROR] Annotators have a circular dependency.
Error message
[INTERNAL ERROR] Annotators have a circular dependency.
What it means
During transitive prerequisite expansion, ensurePrerequisiteAnnotators uses a tick counter as an infinite-loop guard; if the fringe has not emptied after ~1,000,000 iterations it concludes the annotator requirement graph contains a cycle and throws IllegalStateException.
Solutions
- Inspect and fix the requirement definitions so the dependency graph is acyclic.
- Remove custom requirements modifications and revert to stock DEFAULT_REQUIREMENTS.
- Reduce the annotator list to built-ins and re-add custom ones incrementally to isolate the cycle.
- Report/patch the cycle in the requirements map if using a forked CoreNLP.
Example fix
// before
DEFAULT_REQUIREMENTS.put("a", asList("b"));
DEFAULT_REQUIREMENTS.put("b", asList("a")); // cycle
// after
DEFAULT_REQUIREMENTS.put("a", asList("b"));
DEFAULT_REQUIREMENTS.put("b", Collections.emptyList()); Defensive patterns
Strategy: try-catch
Validate before calling
// Detect cycles in requirement map before building pipeline Map<String,Set<String>> g = new HashMap<>(); Annotator.DEFAULT_REQUIREMENTS.forEach((k,v) -> g.put(k, new HashSet<>(v))); // topological sort / DFS color-marking to find a cycle
Try / catch
try { pipeline = new StanfordCoreNLP(props); } catch (IllegalStateException e) { if (e.getMessage().contains("circular dependency")) { resetRequirementOverrides(); pipeline = new StanfordCoreNLP(props); } else throw e; } Prevention
- Never create reciprocal entries in DEFAULT_REQUIREMENTS.
- Keep requirement overrides in one reviewed place.
- Add a unit test that topologically sorts the requirement graph.
When it happens
Trigger: Annotator A requires B and B (transitively) requires A in Annotator.DEFAULT_REQUIREMENTS - normally only reachable with custom requirement definitions or corrupted/patched requirement maps.
Common situations: Custom requirement overrides creating a cycle; bugs after modifying DEFAULT_REQUIREMENTS programmatically; extremely large chains misinterpreted as cycles (rare).
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Unknown annotator
- Cannot infer requirements for annotator
- Unsatisfiable annotator list
- Could not pre-load annotators in server; encountered…
- Must supply a target label to compute precision and recall…
AI-assisted analysis of stanfordnlp/CoreNLP@1b7edd19c4 (2026-09-10).
Data as JSON: /api/errors/f9c5205a7cd86375.
Report an issue: GitHub.
Appendix: source
Thrown at src/edu/stanford/nlp/pipeline/StanfordCoreNLP.java:505
Set<String> unorderedAnnotators = new LinkedHashSet<>(); // linked to preserve order
Collections.addAll(unorderedAnnotators, annotators);
for (String annotator : annotators) {
// Add the annotator
if (!getNamedAnnotators().containsKey(annotator.toLowerCase())) {
throw new IllegalArgumentException("Unknown annotator: " + annotator);
}
// Add its transitive dependencies
unorderedAnnotators.add(annotator.toLowerCase());
if (!Annotator.DEFAULT_REQUIREMENTS.containsKey(annotator.toLowerCase())) {
throw new IllegalArgumentException("Cannot infer requirements for annotator: " + annotator);
}
Queue<String> fringe = new LinkedList<>(Annotator.DEFAULT_REQUIREMENTS.get(annotator.toLowerCase()));
int ticks = 0;
while (!fringe.isEmpty()) {
ticks += 1;
if (ticks == 1000000) {
throw new IllegalStateException("[INTERNAL ERROR] Annotators have a circular dependency.");
}
String prereq = fringe.poll();
unorderedAnnotators.add(prereq);
fringe.addAll(Annotator.DEFAULT_REQUIREMENTS.get(prereq.toLowerCase()));
}
}
if (useParseForPos) {
unorderedAnnotators.remove(Annotator.STANFORD_POS);
}
// Order the annotators
List<String> orderedAnnotators = new ArrayList<>();
while (!unorderedAnnotators.isEmpty()) {
boolean somethingAdded = false; // to make sure the dependencies are satisfiable
// Loop over candidate annotators to add
Iterator<String> iter = unorderedAnnotators.iterator();
while (iter.hasNext()) {View on GitHub (pinned to 1b7edd19c4)