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

  1. Ensure the XML string contains the complete document including all closing '>' characters
  2. Check that all stream/file reads consume the entire document before parsing
  3. Run the input through a strict XML parser (DocumentBuilder) to detect truncation with a clearer message
  4. 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

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


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)