iBotPeaches/Apktool · error · XmlPullParserException

Expected start or end tag.

Error message

Expected start or end tag.

What it means

nextTag() requires the next (non-whitespace-text) event to be START_TAG or END_TAG; it is the convenience method for tag-only iteration. Encountering TEXT with non-whitespace content, or END_DOCUMENT, throws this error.

Source

Thrown at brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/BinaryXmlResourceParser.java:573

        if (eventType != TEXT) {
            throw new XmlPullParserException("Parser must be on TEXT or END_TAG to read text.", this, null);
        }
        String result = getText();
        eventType = next();
        if (eventType != END_TAG) {
            throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.", this, null);
        }
        return result;
    }

    @Override
    public int nextTag() throws XmlPullParserException, IOException {
        int eventType = next();
        if (eventType == TEXT && isWhitespace()) {
            eventType = next();
        }
        if (eventType != START_TAG && eventType != END_TAG) {
            throw new XmlPullParserException("Expected start or end tag.", this, null);
        }
        return eventType;
    }

    // Utility methods

    private Attribute getAttribute(int index) {
        if (mEventType != START_TAG) {
            throw new IndexOutOfBoundsException("Parser must be on START_TAG to get attributes.");
        }
        if (mAttributes == null || index < 0 || index >= mAttributes.length) {
            throw new IndexOutOfBoundsException(
                String.format("Attribute index out of range: index=%s, length=%s",
                    index, mAttributes != null ? mAttributes.length : 0));
        }
        return mAttributes[index];
    }

View on GitHub (pinned to 79b63384d7)

Solutions

  1. Bound loops on the specific END_TAG of the parent element, not just END_DOCUMENT, so nextTag() is never called at EOF.
  2. For mixed content, use next() and filter events instead of nextTag().
  3. Skip whitespace-only TEXT is handled internally — real text means restructure the traversal.
  4. During development, log getEventType()/getName() before each nextTag() to find the offending position.

Example fix

// before
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
    parser.nextTag(); // throws at EOF / on text
}

// after
parser.require(XmlPullParser.START_TAG, null, "root");
while (parser.nextTag() == XmlPullParser.START_TAG) {
    // handle element
    // consume to its END_TAG:
    while (parser.next() != XmlPullParser.END_TAG) { /* skip */ }
}
Defensive patterns

Strategy: validation

Validate before calling

int ev = parser.next();
if (ev == XmlPullParser.TEXT && parser.isWhitespace()) ev = parser.next();
if (ev != XmlPullParser.START_TAG && ev != XmlPullParser.END_TAG) {
    // not tag-only content — use next() traversal instead of nextTag()
}

Try / catch

try {
    int ev = parser.nextTag();
} catch (XmlPullParserException e) {
    if (e.getMessage().equals("Expected start or end tag.")) {
        // encountered real text or EOF — restructure the loop bounds
    }
}

Prevention

When it happens

Trigger: Calling nextTag() on XML that contains character data between tags (mixed content), or reaching the end of the document — e.g. unconditionally calling nextTag() in a loop that runs past the final END_TAG.

Common situations: Tag-walking loops like `while (parser.getEventType() != END_DOCUMENT) parser.nextTag();` that hit trailing text or EOF; parsing AXML with text nodes where the developer assumed tags only; malformed event streams.

Related errors


AI-assisted analysis of iBotPeaches/Apktool@79b63384d7 (2026-08-14). Data as JSON: /api/errors/de26a3c0041367e4. Report an issue: GitHub.