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

  1. Strip any tags between </RECORD> and the next <RECORD> other than the single final </EDITS>.
  2. If records were wrapped in another element (e.g. <RECORDS>), remove the wrapper — only a flat record list is legal.
  3. Rebuild the file by concatenating whole <RECORD>...</RECORD> blocks inside one <EDITS>...</EDITS> envelope.
  4. 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

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.