Skip to content

avoid_unmodified_loop_condition

v1.0.0 Warning Control Flow

This rule flags a while or do/while loop whose condition reads only variables that the body never assigns. The condition evaluates the same way forever, so the loop either never runs or never stops.

An infinite loop is not a subtle failure — it hangs the isolate, freezes the UI, and in a Flutter app looks like a crash. But the cause is subtle: a forgotten i++, or an increment applied to the wrong variable in a loop that reads a different one.

Static detection is possible because the condition and the body are right next to each other. If no variable the condition reads is ever written in the body, no execution can change the outcome.

See also: Dart: loops

var i = 0;
while (i < items.length) {
print(items[i]); // `i` is never advanced
}

Advancing the wrong variable is the same bug wearing a disguise:

var i = 0;
var j = 0;
while (i < limit) {
j++; // `i` still never changes
}
var i = 0;
while (i < items.length) {
print(items[i]);
i++;
}

Or use a construct that advances for you:

for (final item in items) {
print(item);
}

The rule is deliberately narrow, because the cost of a false positive here is high.

while (true) is not reported — it is the idiomatic infinite loop, ended by a break. Any break, return or throw in the body suppresses the report for the same reason: the loop has an exit the condition does not control.

Only locals and parameters are tracked. A condition that reads a field, calls a method, accesses a property, or awaits is treated as opaque: those can change without an assignment in the body, so no conclusion is safe. A closure anywhere in the body also suppresses the report, since it may mutate a captured variable when invoked.

This rule is in the core preset, so it is on with preset: core, preset: recommended or preset: opinionated.

To turn it off:

many_lints.yaml
rules:
avoid_unmodified_loop_condition: false

To keep the rule on but skip certain paths, use per-rule exclude.