Tencent/QMUI_Android · error · IllegalArgumentException

end must > start

Error message

end must > start

What it means

QMUIQQFaceCompiler.compile() requires end > start when slicing the CharSequence; passing end <= start is rejected with an IllegalArgumentException. Note that end larger than text length is tolerated (clamped to size), so the error only fires for end <= start.

Source

Thrown at qmui/src/main/java/com/qmuiteam/qmui/qqface/QMUIQQFaceCompiler.java:100

        if (QMUILangHelper.isNullOrEmpty(text)) {
            return null;
        }
        return compile(text, 0, text.length());
    }

    public ElementList compile(CharSequence text, int start, int end) {
        return compile(text, start, end, false);
    }

    private ElementList compile(CharSequence text, int start, int end, boolean inSpan) {
        if (QMUILangHelper.isNullOrEmpty(text)) {
            return null;
        }
        if (start < 0 || start >= text.length()) {
            throw new IllegalArgumentException("start must >= 0 and < text.length");
        }
        if (end <= start) {
            throw new IllegalArgumentException("end must > start");
        }
        int size = text.length();

        if (end > size) {
            end = size;
        }

        boolean hasClickableSpans = false;
        QMUITouchableSpan[] spans = null;
        int[] spanInfo = null;
        if (!inSpan && (text instanceof Spannable)) {
            final Spannable spannable = (Spannable) text;
            spans = ((Spannable) text).getSpans(
                    0,
                    text.length() - 1,
                    QMUITouchableSpan.class);
            Arrays.sort(spans, new Comparator<QMUITouchableSpan>() {
                @Override

View on GitHub (pinned to 026e7d4866)

Solutions

  1. Ensure end > start before invoking; skip empty ranges instead of compiling them.
  2. Recompile the QMUIQQFaceView after any text/Spannable mutation so offsets stay consistent.
  3. Validate span boundaries: only call createTouchSpanElement/compile when spanEnd > spanStart.
  4. Clamp end to text.length() yourself if unsure — values greater than length are safe (they get clamped internally).

Example fix

// before
int spanEnd = spannable.getSpanEnd(span);
compiler.compile(text, start, spanEnd, true); // spanEnd may be <= start

// after
int spanEnd = spannable.getSpanEnd(span);
if (spanEnd > start) {
    compiler.compile(text, start, spanEnd, true);
}
Defensive patterns

Strategy: validation

Validate before calling

if (end > start) {
    compiler.compile(text, start, end, inSpan);
}

Type guard

boolean isRangeValid(CharSequence text, int start, int end) {
    return start >= 0 && end > start && start < text.length();
}

Try / catch

try {
    compiler.compile(text, start, end, true);
} catch (IllegalArgumentException e) {
    Log.w(TAG, "invalid span range " + start + ".." + end, e);
}

Prevention

When it happens

Trigger: Calling a compile overload with an explicit end that is <= start; span offsets (getSpanStart/getSpanEnd) inverted because the Spannable was edited after spans were computed; recursion passing a zero-length range.

Common situations: Measuring a touch span over text that was mutated after compilation, or computing end as start + 0 for an empty/deleted span region.

Related errors


AI-assisted analysis of Tencent/QMUI_Android@026e7d4866 (2026-09-06). Data as JSON: /api/errors/b00596af1316975c. Report an issue: GitHub.