Why Java says content is not allowed in prolog
Java’s built-in parser (Xerces) throws Content is not allowed in prolog when anything other than markup or whitespace comes before the root element. Causes, most common first:
1. A byte order mark read as text. The parser skips a UTF-8 BOM when it reads bytes, not when it gets a String or Reader. Files.readString() and HttpResponse.BodyHandlers.ofString() keep it as U+FEFF, and decoding with ISO-8859-1 or Windows-1252 turns it into , as in the checker above.
2. The body is not XML. A JSON error such as {"error": "invalid_token"}, a plain-text Unauthorized, or a file path passed as content: new InputSource(new StringReader("config/orders.xml")) parses the path itself.
3. Text in front of the XML. A log prefix, a stray character left by a template, a dash.
Close relatives have their own messages. Whitespace or a comment before <?xml gives The processing instruction target matching "[xX][mM][lL]" is not allowed. An HTML page usually gets past the prolog and fails later, for example on <meta charset="utf-8"> with The element type "meta" must be terminated by the matching end-tag "</meta>". An empty body gives Premature end of file.
How to fix it
Look at the first bytes
head -c 16 invoice.xml | od -c
0000000 357 273 277 < ? x m l v e r s i o n
357 273 277 is a UTF-8 BOM; any other character before < is the stray content. In PowerShell, Format-Hex invoice.xml | Select-Object -First 1 shows the BOM as EF BB BF.
Give the parser bytes
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(path.toFile()); // reads bytes and skips a BOM
For HTTP, check the status and the type, then pass the stream:
HttpResponse<InputStream> res = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
String type = res.headers().firstValue("Content-Type").orElse("");
if (res.statusCode() != 200 || !type.contains("xml")) {
String preview = new String(res.body().readNBytes(200), StandardCharsets.UTF_8);
throw new IOException(res.statusCode() + " " + type + ": " + preview);
}
Document doc = builder.parse(res.body());
A 401 with a JSON body now fails with 401 application/json: {"error": ...} instead of a parser error.
When you only have a String
if (xml.startsWith("\uFEFF")) xml = xml.substring(1);
xml = xml.stripLeading();
stripLeading() also removes whitespace before <?xml. It does not remove U+FEFF, hence the first line.
JAXB, Spring and other libraries that parse XML for you wrap this same SAXParseException; look for it in the cause chain and apply the fixes above. The XML validator reports text before the root element and a misplaced <?xml declaration with their line and column.
Same error in other languages
- .NET:
System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1. - libxml2 (Python lxml, PHP, xmllint):
Start tag expected, '<' not found - Python ElementTree:
not well-formed (invalid token) - The reverse: a JSON parser that gets XML reports invalid character ‘<’ looking for beginning of value in Go and Unexpected token ‘<’ in JavaScript.
JSON parse errors, by language: common JSON errors.