Skip to content
TaeyoungKim.dev

Dart String? and !: Why a Null Assertion Fails at Runtime

AppWritten 3 min readTaeyoungKim
LinkedInX

The screen title has not arrived yet, but the app tries to read title!.length and throws. The ! may look as if it removes null; it actually asserts “this expression is not null here.” If the assertion is wrong, execution fails. A value held in String? can still be absent.

What do String? and ! each mean?

The diagram compares a present string with null. Applying ! to null throws; ?. and ?? can instead choose the default length zero in this example.

String does not allow null, while String? can hold a string or null. ! is not a new type suffix here: it is an operator applied after a nullable expression. title! asserts that the current value of title is non-null. If it is null, the assertion fails at runtime rather than producing a string.

dart
String? readLabel(bool found) => found ? 'Guide' : null;
int requiredLength(String? input) => input!.length;

void main() {
  final String? label = readLabel(false);
  print(label?.length ?? 0);

  try {
    print(requiredLength(label));
  } catch (error) {
    print(error.runtimeType);
  }
}

The first output is 0: ?. reads length only when there is a value, and ?? 0 supplies a default otherwise. The second path reaches input! with null and prints the runtime error type in this demonstration. Real screen code should normally handle this state instead of deliberately throwing.

Decide what an absent value should mean

If zero or replacement text is a legitimate result, use ??. If the screen should wait or ask for input, branch with if (label == null) and use the value only afterward. Reserve ! for an invariant that is actually guaranteed by another boundary. Adding it merely to silence a compiler warning defers the problem to runtime.

dart
String labelText(String? label) {
  if (label == null) return 'No title';
  return label;
}

This function makes the null behavior explicit. If a title is genuinely optional while the user is still typing, the branch is easier to understand than !. If a title is required by the data model, validate it at the input boundary and consider passing a non-nullable type deeper into the app.

Where should you investigate the error?

Find the expression immediately before !, then trace when it receives a value and which paths can leave it null. Test both presence and absence, including moments before asynchronous loading, deselection, and missing data. One happy-path sample will not expose the error.

The Dart operator documentation defines the null assertion. Rather than scattering ! to suppress a production error, choose where the value becomes required and validate it there.

Key takeaways: ! does not repair null

String? permits an absent value; value! asserts that a value is present. A false assertion throws at runtime. Decide whether absence is normal or erroneous, then choose ?., ??, an explicit branch, or input validation accordingly.

Author

TaeyoungKim

Connecting technical foundations with implementation, verification, and production decisions.

#Dart#null safety#String?#null assertion#runtime error

Read next