Activiti/Activiti · error · JSONException
Misshaped element
Error message
Misshaped element
What it means
Thrown by XMLTokener.nextToken() when it reaches end-of-input while looking for the next token inside an element's angle brackets. After '<' opens a tag, the parser needs at least a name and a closing '>' but the document ends first. Equivalent to an unclosed tag at end of input.
Solutions
- Ensure the XML string contains the complete document including all closing '>' characters
- Check that all stream/file reads consume the entire document before parsing
- Run the input through a strict XML parser (DocumentBuilder) to detect truncation with a clearer message
- If consuming incrementally, accumulate until the root element is closed before converting
Example fix
// before String xml = "<process id=\"p1\""; // tag never closed XML.toJSONObject(xml); // throws // after String xml = "<process id=\"p1\"/>"; XML.toJSONObject(xml);
Defensive patterns
Strategy: validation
Validate before calling
public static boolean isCompleteDocument(String xml) {
String s = xml == null ? "" : xml.trim();
return s.startsWith("<") && s.endsWith(">")
&& javax.xml.XMLConstants.class != null; // plus a strict check below
}
// stronger: try { DocumentBuilderFactory.newInstance().newDocumentBuilder()
// .parse(new InputSource(new StringReader(xml))); return true; } catch (...) { return false; } Try / catch
try {
JSONObject obj = XML.toJSONObject(xml);
} catch (JSONException e) {
if (e.getMessage().contains("Misshaped element")) {
throw new IllegalArgumentException("XML input is truncated: a tag was left unclosed", e);
}
throw e;
} Prevention
- Read files/streams fully (loop until EOF) before parsing, don't pass partial buffers
- Check that streamed downloads match the expected byte count
- Log the tail of the input string when parse errors occur to spot truncation
- Detect truncation with DocumentBuilder before using the JSON converter
When it happens
Trigger: XML.toJSONObject / JSONML.toJSONObject on text like '<foo' or '<a href="x"' — an opening '<' with no '>' before the string ends, often from truncated responses or files.
Common situations: Partial downloads of XML files; SAX/stream readers that pass partial buffers; logs where the trailing '>' was clipped; pipeline tools writing XML without final flush.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Bad character in a name
- Misplaced '<'
- Misshaped meta tag
- Unterminated string
- Expected a closing name instead of '" + token + "'.
AI-assisted analysis of Activiti/Activiti@56435b1a97 (2026-09-09).
Data as JSON: /api/errors/4f0293dbe3b3fe1f.
Report an issue: GitHub.
Appendix: source
Thrown at activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/util/json/XMLTokener.java:241
/**
* Get the next XML Token. These tokens are found inside of angle brackets. It may be one of these characters: <code>/ > = ! ?</code> or it may be a string wrapped in single quotes or double quotes,
* or it may be a name.
*
* @return a String or a Character.
* @throws JSONException
* If the XML is not well formed.
*/
public Object nextToken() throws JSONException {
char c;
char q;
StringBuffer sb;
do {
c = next();
} while (Character.isWhitespace(c));
switch (c) {
case 0:
throw syntaxError("Misshaped element");
case '<':
throw syntaxError("Misplaced '<'");
case '>':
return XML.GT;
case '/':
return XML.SLASH;
case '=':
return XML.EQ;
case '!':
return XML.BANG;
case '?':
return XML.QUEST;
// Quoted string
case '"':
case '\'':
q = c;
sb = new StringBuffer();View on GitHub (pinned to 56435b1a97)