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

  1. Give every record its <DATA>...</DATA> block, copied verbatim from the original oev output.
  2. Delete whole <RECORD> blocks instead of just their DATA parts when filtering.
  3. 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

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


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/ad22b0b6ec99be19. Report an issue: GitHub.