apache/hadoop · error · InvalidXmlException
expected </EDITS> or </RECORD>
Error message
expected </EDITS> or </RECORD>
What it means
Thrown from endElement() in EXPECT_RECORD state: at the top level of the document (between two <RECORD> elements, or where </EDITS> should close the file) a closing tag other than </RECORD> or </EDITS> appeared. The format only allows a flat sequence of <RECORD> elements between the version header and </EDITS>; any other element at that depth is rejected.
Source
Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineEditsViewer/OfflineEditsXmlLoader.java:205
throw new InvalidXmlException("expected <EDITS/>");
case EXPECT_VERSION:
if (!name.equals("EDITS_VERSION")) {
throw new InvalidXmlException("expected </EDITS_VERSION>");
}
try {
int version = Integer.parseInt(str);
visitor.start(version);
} catch (IOException e) {
// Can't throw IOException from a SAX method, sigh.
throw new RuntimeException(e);
}
state = ParseState.EXPECT_RECORD;
break;
case EXPECT_RECORD:
if (name.equals("EDITS")) {
state = ParseState.EXPECT_END;
} else if (!name.equals("RECORD")) {
throw new InvalidXmlException("expected </EDITS> or </RECORD>");
}
break;
case EXPECT_OPCODE:
if (!name.equals("OPCODE")) {
throw new InvalidXmlException("expected </OPCODE>");
}
opCode = FSEditLogOpCodes.valueOf(str);
state = ParseState.EXPECT_DATA;
break;
case EXPECT_DATA:
throw new InvalidXmlException("expected <DATA/>");
case HANDLE_DATA:
stanza.setValue(str);
if (stanzaStack.empty()) {
if (!name.equals("DATA")) {
throw new InvalidXmlException("expected </DATA>");
}
state = ParseState.EXPECT_RECORD;View on GitHub (pinned to 2add963021)
Solutions
- Strip any tags between </RECORD> and the next <RECORD> other than the single final </EDITS>.
- If records were wrapped in another element (e.g. <RECORDS>), remove the wrapper — only a flat record list is legal.
- Rebuild the file by concatenating whole <RECORD>...</RECORD> blocks inside one <EDITS>...</EDITS> envelope.
- When in doubt, regenerate the XML from the binary edit log with hdfs oev.
Example fix
// before <EDITS><EDITS_VERSION>-63</EDITS_VERSION> <RECORDS> <RECORD>...</RECORD> </RECORDS> </EDITS> // after <EDITS><EDITS_VERSION>-63</EDITS_VERSION> <RECORD>...</RECORD> </EDITS>
Defensive patterns
Strategy: try-catch
Validate before calling
private static boolean flatRecordList(Path p) throws IOException {
// top level between records must contain only <RECORD> opens and the final </EDITS>
List<String> lines = Files.readAllLines(p, StandardCharsets.UTF_8);
for (String l : lines) {
String t = l.trim();
if (t.startsWith("</") && !t.startsWith("</RECORD>") && !t.startsWith("</EDITS>")
&& !t.startsWith("</EDITS_VERSION>")) return false;
}
return true;
} Try / catch
try {
loader.loadEdits();
} catch (InvalidXmlException e) {
// stray close tag at top level; re-splice whole RECORD blocks only
LOG.error("Illegal top-level tag in edits XML: {}", e.getMessage());
} Prevention
- Splice only whole <RECORD>...</RECORD> blocks inside one <EDITS> envelope.
- Do not invent wrapper elements around records.
- After any sed/awk surgery, diff the tag balance (count opens vs closes per name).
When it happens
Trigger: A stray closing tag between records, e.g. a leftover </RECORDS> after records were wrapped in a container element, or any orphan close tag left by splicing XML fragments at top level. startElement in this state throws a different message, so this specific text means an unexpected close at record level.
Common situations: Concatenating or splicing oev XML outputs with sed/awk leaving orphan close tags; wrapping records in a made-up container element; renaming <EDITS> in the footer but not the header.
Related errors
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/573729ab29454f77.
Report an issue: GitHub.