languagetool-org/languagetool · error · RuntimeException
Could not parse XML: ${xml}
Error message
Could not parse XML: ${xml} What it means
AfterTheDeadlineChecker wraps any exception thrown while parsing the After the Deadline service's XML response into a RuntimeException with the raw XML appended. It is thrown in getDocument when DocumentBuilder.parse fails, meaning the HTTP response body was not well-formed XML. The raw response is included in the message to aid debugging of malformed or unexpected server output.
Source
Thrown at languagetool-wikipedia/src/main/java/org/languagetool/dev/dumpcheck/AfterTheDeadlineChecker.java:113
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList errors = (NodeList)xPath.evaluate("//error", document, XPathConstants.NODESET);
for (int i = 0; i < errors.getLength(); i++) {
Node error = errors.item(i);
String string = xPath.evaluate("string", error);
String description = xPath.evaluate("description", error);
matches.add(description + ": " + string);
}
return matches;
}
private Document getDocument(String xml) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xml));
return builder.parse(inputSource);
} catch (Exception e) {
throw new RuntimeException("Could not parse XML: " + xml, e);
}
}
public static void main(String[] args) throws Exception {
if (args.length < 4) {
System.out.println("Usage: " + AfterTheDeadlineChecker.class.getSimpleName() + " <langCode> <atdUrlPrefix> <file...>");
System.out.println(" <langCode> a language code like 'en' for English");
System.out.println(" <atdUrlPrefix> URL prefix of After the Deadline server, like 'http://localhost:1059/checkDocument?data='");
System.out.println(" <sentenceLimit> Maximum number of sentences to check, or 0 for no limit");
System.out.println(" <file...> Wikipedia and/or Tatoeba file(s)");
System.exit(1);
}
Language language = Languages.getLanguageForShortCode(args[0]);
String urlPrefix = args[1];
int maxSentenceCount = Integer.parseInt(args[2]);
List<String> files = Arrays.asList(args).subList(3, args.length);
AfterTheDeadlineChecker atdChecker = new AfterTheDeadlineChecker(urlPrefix, maxSentenceCount);
atdChecker.run(language, files);View on GitHub (pinned to 2e990059ce)
Solutions
- Print/log the raw XML from the exception message to see what was actually returned
- Verify the atdUrlPrefix points to a working After the Deadline instance (test the URL in a browser/curl)
- Check the ATD server is up and not returning an HTML error page (HTTP status, proxy)
- Validate encoding of the response and that it is complete (no truncation)
Example fix
// before
Document doc = checker.getDocument(xml);
// after
if (xml == null || xml.trim().isEmpty() || !xml.trim().startsWith("<?xml") && !xml.trim().startsWith("<")) {
throw new IllegalStateException("ATD returned non-XML response: " + StringUtils.abbreviate(xml, 200));
}
Document doc = checker.getDocument(xml); Defensive patterns
Strategy: validation
Validate before calling
if (xml == null || xml.trim().isEmpty() || !(xml.trim().startsWith("<?xml") || xml.trim().startsWith("<"))) {
throw new IllegalStateException("ATD did not return XML: " + StringUtils.abbreviate(xml, 200));
} Type guard
static boolean looksLikeXml(String s) {
return s != null && s.trim().startsWith("<");
} Try / catch
try {
Document doc = checker.getDocument(xml);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Could not parse XML")) {
log.warn("ATD response not parseable, skipping", e);
} else throw e;
} Prevention
- Log HTTP status and first bytes of every ATD response
- Curl the atdUrlPrefix before batch runs
- Guard against proxies/captive portals injecting HTML
- Set explicit charset on requests
When it happens
Trigger: The ATD server returns HTML (e.g. an error page, proxy block page, or 404 body) instead of XML; the response is truncated or empty; or the response contains characters invalid in XML (bad encoding).
Common situations: Wrong atdUrlPrefix pointing at a non-ATD endpoint or behind a captive proxy; ATD server temporarily down returning an error page; network middleware injecting content; version changes in the ATD API output format.
Related errors
- id is null for rule with name '
- Cannot load or parse input stream of '${filename}'
- Could not load simple replacement data from: " + path + ". E
- Format error in file " + path + ", line: " + line
- '<marker>' may not be nested in rule '
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/6923ad8cc5928815.
Report an issue: GitHub.