Skip to content

avoid_non_null_assertion

v1.0.0WarningConfigurableCode Quality

This rule flags every use of the null-assertion operator !.

! is not a check — it is an unchecked assertion. It tells the compiler a value is non-null and throws a TypeError at runtime when that turns out to be wrong.

The type system stops helping at exactly the point the value is most likely to be null. Every other tool Dart gives you for nullability — ?., ??, a null check, pattern matching — either handles the null case or makes the compiler prove it cannot happen. ! alone converts a question the analyser could have answered into a production crash.

See also: Understanding null safety, Sound null safety

// Throws if `user` is null — nothing checked it.
user!.name;
// Chained bangs compound the risk: either one can throw.
config.timeout!.inSeconds;
fetchAccount()!.close();
// Short-circuit instead of throwing.
user?.name;
// Supply a fallback.
final seconds = config.timeout?.inSeconds ?? 0;
// Or narrow with a check, so the compiler proves it.
if (user != null) {
print(user.name);
}

Reading a known-present entry out of a Map is exempt by default, because Map’s index operator is declared nullable no matter what the map holds. The pedantic preset disables this exception and therefore bans every postfix !:

// Not reported.
translations['title']!.toUpperCase();

The SDK linter has no equivalent rule — a request for one (sdk#59476) was closed without shipping. Two official rules cover strict subsets of what this rule reports, so enabling both means those cases are reported twice:

Official rule Overlap
unnecessary_null_checks Flags only bangs whose surrounding context already accepts null — the redundant ones
null_check_on_nullable_type_parameter Flags only a bang on a potentially nullable generic type parameter

If the duplicate diagnostics bother you, disable the narrower SDK rules — this rule already reports everything they do.

There is deliberately no quick fix. Rewriting ! to ?. changes behaviour: an expression that used to throw would instead silently evaluate to null, which moves the failure somewhere else rather than fixing it. Choosing between ?., ??, a guard, or restructuring the types is a judgement call the author has to make.

For the common case of a ! on a field inside an if (field != null) guard, two assists do the rewrite for you — Convert null check to pattern (semantics-preserving) and Convert null check to destructuring pattern (narrows the condition). They are assists rather than fixes because both restructure the whole if, above the node this rule reports on.

A ! inside a pattern (case var x!) is a NullAssertPattern, not the postfix operator, and is not reported.

This rule is in the opinionated preset, so it is on with preset: opinionated, or by name:

many_lints.yaml
rules:
avoid_non_null_assertion: true

To turn it off again:

many_lints.yaml
rules:
avoid_non_null_assertion: false

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

analysis_options.yaml
many_lints:
rules:
avoid_non_null_assertion:
ignore_checked_fields: true
ignore_map_indexes: false
Option Type Default Description
ignore_checked_fields bool false Don’t report a ! on a field an enclosing if (field != null) already checked. Off by default because type promotion does not apply to fields — the bang is genuinely what makes the code compile, and the field can still change between the check and the use
ignore_map_indexes bool true Allow the idiomatic map[key]! spelling. The pedantic preset changes this default to false