apache/hadoop · error · InvalidXmlException
expected <DATA/>
Error message
expected <DATA/>
What it means
Thrown from endElement() in EXPECT_DATA state: the <OPCODE> element closed (its text was consumed as the op code) but the next event was a close tag instead of the required <DATA> opening. The loader mandates a <DATA> stanza immediately after OPCODE in every record; a record ending right after </OPCODE> is rejected.
Source
Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineEditsViewer/OfflineEditsXmlLoader.java:216
}
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;
} finally {
if (stanza != null) {
System.err.println("fromXml error decoding opcode " + opCode +
"\n" + stanza.toString());
stanza = null;
}View on GitHub (pinned to 2add963021)
Solutions
- Give every record its <DATA>...</DATA> block, copied verbatim from the original oev output.
- Delete whole <RECORD> blocks instead of just their DATA parts when filtering.
- Regenerate the XML from the binary edit log with hdfs oev.
Example fix
// before <RECORD> <OPCODE>OP_MKDIR</OPCODE> </RECORD> // after <RECORD> <OPCODE>OP_MKDIR</OPCODE> <DATA><TXID>1</TXID><PATH>/tmp</PATH><TIMESTAMP>...</TIMESTAMP><OWNER>hdfs</OWNER><GROUP>hdfs</GROUP><PERMISSION_STRING>755</PERMISSION_STRING></DATA> </RECORD>
Defensive patterns
Strategy: try-catch
Validate before calling
private static boolean recordHasData(String xml) {
for (String rec : xml.split("(?s)(?=<RECORD>)")) {
if (rec.startsWith("<RECORD") && rec.contains("<OPCODE>") && !rec.contains("<DATA")) {
return false;
}
}
return true;
} Try / catch
try {
loader.loadEdits();
} catch (InvalidXmlException e) {
// record ended after </OPCODE> with no <DATA>; restore the DATA stanza
LOG.error("Record missing DATA element: {}", e.getMessage());
} Prevention
- Treat OPCODE and DATA as an inseparable pair inside every RECORD.
- Do not strip DATA blocks to slim files — delete whole records instead.
- Test generators against a tiny known-good file round-tripped through oev.
When it happens
Trigger: A record of the form <RECORD><OPCODE>OP_ADD</OPCODE></RECORD> — the </RECORD> close arrives while the parser waits for <DATA> to open. Any close tag between </OPCODE> and <DATA> has the same effect.
Common situations: Hand-trimming 'uninteresting' DATA blocks to shrink files; scripts that strip DATA elements; edits XML produced by third-party generators that omit DATA.
Related errors
- expected </OPCODE>
- 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/ad22b0b6ec99be19.
Report an issue: GitHub.