{"record":{"id":"1729fa34f545f1a2","repo":"stanfordnlp/CoreNLP","slug":"unexpected-class-in-phrase-table","errorCode":null,"errorMessage":"Unexpected class in phrase table ","messagePattern":"Unexpected class in phrase table ","errorType":"exception","errorClass":"java.lang.RuntimeException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/ling/tokensregex/PhraseTable.java","lineNumber":871,"sourceCode":"    public PhraseTableIterator(PhraseTable phraseTable) {\n      this.phraseTable = phraseTable;\n      this.iteratorStack.push(this.phraseTable.rootTree.values().iterator());\n      this.next = getNext();\n    }\n\n    private Phrase getNext() {\n      while (!iteratorStack.isEmpty()) {\n        Iterator<Object> iter = iteratorStack.peek();\n        if (iter.hasNext()) {\n          Object obj = iter.next();\n          if (obj instanceof Phrase) {\n            return (Phrase) obj;\n          } else if (obj instanceof Map) {\n            iteratorStack.push(((Map) obj).values().iterator());\n          } else if (obj instanceof List) {\n            iteratorStack.push(((List) obj).iterator());\n          } else {\n            throw new RuntimeException(\"Unexpected class in phrase table \" + obj.getClass());\n          }\n        } else {\n          iteratorStack.pop();\n        }\n      }\n      return null;\n    }\n\n    @Override\n    public boolean hasNext() {\n      return next != null;\n    }\n\n    @Override\n    public Phrase next() {\n      Phrase res = next;\n      next = getNext();\n      return res;","sourceCodeStart":853,"sourceCodeEnd":889,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/ling/tokensregex/PhraseTable.java#L853-L889","documentation":"The PhraseTable iterator walks the trie, pushing iterators for Map and List nodes and returning Phrase leaves. When it encounters a value that is neither Phrase, Map, nor List, it throws this RuntimeException - the table's structure is corrupted or contains an unsupported node type.","triggerScenarios":"Calling iterator() (e.g. via iterating the table or collecting phrases) on a table whose internal maps contain a foreign object type.","commonSituations":"External mutation of the root tree via reflection; concurrent addPhrase during iteration; deserialized tables from a different version.","solutions":["Rebuild the PhraseTable with addPhrases and never mutate its internals directly.","Do not iterate while another thread is adding phrases; finish writes first.","Use the public API (addPhrase, lookup) instead of touching internal structures.","Report to Stanford NLP with the reported class name if the table was built normally."],"exampleFix":"// before\nfor (Object o : table) { ... } // while another thread calls addPhrase\n// after\n// finish all addPhrase calls, then\nfor (Object o : table) { ... }","handlingStrategy":"try-catch","validationCode":"// no concurrent writes while iterating\nassert !isBuilding.get() : \"cannot iterate while phrases are being added\";","typeGuard":"boolean isIterableNode(Object o) { return o instanceof Phrase || o instanceof Map || o instanceof List; }","tryCatchPattern":"try { for (Object p : table) { ... } } catch (RuntimeException e) { if (e.getMessage().contains(\"Unexpected class in phrase table\")) { table = rebuildTable(phrases); } else { throw e; } }","preventionTips":["Finish all writes before iterating.","Use table.iterator()/getPhrases() rather than internal structures.","Rebuild the table if any corruption is detected."],"tags":["tokensregex","internal-state","iteration"],"backgroundTag":"internal-invariant-violation","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}