apache/hadoop · error · InvalidXmlException
expected </OPCODE>
Error message
expected </OPCODE>
What it means
Thrown from endElement() in EXPECT_OPCODE state: a <RECORD> was opened but the next element-close event was not </OPCODE>. Every <RECORD> must begin with exactly <OPCODE>OP_XXX</OPCODE> (its text is resolved via FSEditLogOpCodes.valueOf); a record that closes before a complete OPCODE element is rejected.
Source
Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineEditsViewer/OfflineEditsXmlLoader.java:210
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;
FSEditLogOp op = opCache.get(opCode);
opCode = null;
try {
op.decodeXml(stanza);
stanza = null;View on GitHub (pinned to 2add963021)
Solutions
- Delete empty <RECORD/> or <RECORD></RECORD> blocks entirely — records are optional, but when present they must be complete.
- Ensure every record reads <RECORD><OPCODE>OP_...</OPCODE><DATA>...</DATA></RECORD>.
- Regenerate the file with hdfs oev rather than filtering the XML by hand.
Example fix
// before <RECORD> <OPCODE>OP_ADD</OPCODE> </RECORD> <RECORD/> // after <RECORD> <OPCODE>OP_ADD</OPCODE> <DATA>...</DATA> </RECORD>
Defensive patterns
Strategy: try-catch
Validate before calling
private static boolean recordsAreComplete(String xml) {
// every RECORD must contain an OPCODE element
for (String rec : xml.split("(?s)(?=<RECORD>)")) {
if (rec.startsWith("<RECORD") && !rec.contains("<OPCODE>")) return false;
}
return true;
} Try / catch
try {
loader.loadEdits();
} catch (InvalidXmlException e) {
// a <RECORD> lacked <OPCODE>...</OPCODE>; drop or complete empty records
LOG.error("Incomplete record in edits XML: {}", e.getMessage());
} Prevention
- Delete whole RECORD blocks when filtering, never just their children.
- Validate that each <RECORD> contains both OPCODE and DATA before conversion.
- Prefer regenerating from the binary edit log over hand-filtering XML.
When it happens
Trigger: An empty or self-closed record element: <RECORD/> or <RECORD></RECORD> — the </RECORD> close fires while the loader still waits for <OPCODE>. Also any record whose first child closes with a name other than OPCODE.
Common situations: Hand-deleting record bodies but leaving empty <RECORD> shells; line-based filtering tools that drop the opcode line but keep the record tags; template-generated edits files with unfilled records.
Related errors
- expected <DATA/>
- expected <EDITS/>
- expected </EDITS_VERSION>
- expected </EDITS> or </RECORD>
- expected </DATA>
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/902b40ad9c3b56e1.
Report an issue: GitHub.