Tencent/QMUI_Android · error · RuntimeException
unsafeAddEffect: start($start) is bigger than end($end)
Error message
unsafeAddEffect: start($start) is bigger than end($end)
What it means
TypeModel.unsafeAddEffect attaches a text effect (typeface, text size, background, color, underline) to a [start, end) character range. It requires start <= end; a start greater than end cannot form a valid element range and throws a RuntimeException immediately, before any element lookup.
Source
Thrown at type/src/main/java/com/qmuiteam/qmui/type/TypeModel.kt:65
fun addTextColorEffect(start: Int, end: Int, textColor: Int): EffectRemover? {
val types: MutableList<Int> = ArrayList()
types.add(TypeEnvironment.TYPE_TEXT_COLOR)
return unsafeAddEffect(start, end, types) { env -> env.textColor = textColor }
}
fun addUnderLineEffect(start: Int, end: Int, underLineColor: Int, underLineHeight: Int): EffectRemover? {
val types: MutableList<Int> = ArrayList()
types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_WIDTH)
types.add(TypeEnvironment.TYPE_BORDER_BOTTOM_COLOR)
return unsafeAddEffect(
start, end, types
) { env -> env.setBorderBottom(underLineHeight, underLineColor) }
}
fun unsafeAddEffect(start: Int, end: Int, types: List<Int>, environmentUpdater: EnvironmentUpdater): EffectRemover? {
if (start > end) {
throw RuntimeException("unsafeAddEffect: start($start) is bigger than end($end)")
}
val elementStart = getByPos(start)
val elementEnd = getByPos(end)
if (elementStart == null || elementEnd == null) {
return null
}
for (type in types) {
elementStart.addSaveType(type)
elementEnd.addRestoreType(type)
}
elementStart.addEnvironmentUpdater(environmentUpdater)
firstEffect = if (firstEffect == null) {
elementStart
} else {
elementStart.insertEffectTo(firstEffect!!)
}
firstEffect = elementEnd.insertEffectTo(firstEffect!!)
return DefaultEffectRemove(this, start, end, types, environmentUpdater)View on GitHub (pinned to 026e7d4866)
Solutions
- Validate start <= end before calling; swap or clamp the range if inverted.
- Recompute the offsets against the current text content (the offsets refer to positions in the TypeModel, not the original string).
- Check the return value as well: if getByPos returns null for either endpoint the call silently no-ops (returns null), so log/verify the effect was applied.
Example fix
// before
typeModel.unsafeAddEffect(end, start, types, updater); // throws when end > start
// after
int s = Math.min(start, end);
int e = Math.max(start, end);
if (s <= e) {
typeModel.unsafeAddEffect(s, e, types, updater);
} Defensive patterns
Strategy: validation
Validate before calling
public static void addEffectSafe(TypeModel m, int start, int end, List<Integer> types, EnvironmentUpdater u) {
if (start > end) throw new IllegalArgumentException("start(" + start + ") > end(" + end + ")");
m.unsafeAddEffect(start, end, types, u);
} Try / catch
try {
typeModel.unsafeAddEffect(start, end, types, updater);
} catch (RuntimeException e) {
Log.w("TypeModel", "invalid effect range: " + start + ".." + end, e);
} Prevention
- Normalize ranges with Math.min/Math.max before applying effects.
- Recompute offsets after any text edit; stale offsets often invert ranges.
- Keep a single helper for effect application that enforces start <= end.
When it happens
Trigger: Calling unsafeAddEffect(start, end, types, updater) — or any wrapper like addTypefaceEffect/addTextSizeEffect/addBgEffect/addTextColorEffect/addUnderLineEffect routed to it — with start > end (e.g. swapped arguments or a range computed against a different string).
Common situations: Computing highlight ranges by searching text after edits, so stale offsets produce inverted ranges; swapping the two positional arguments; applying effects before the text model is updated to match the offsets.
Related errors
- Can not perform LatestVisitRecord, %s must be annotated by L
- Fragment(%s) not attached to Activity.
- Can not find the fragment container provider.
- requestCode can not be %s
- %s did not call through to super.shouldPreventSwipeBack()
AI-assisted analysis of Tencent/QMUI_Android@026e7d4866 (2026-09-06).
Data as JSON: /api/errors/392e0fac810780ef.
Report an issue: GitHub.