Skip to content

avoid_not_encodable_in_to_json

v1.0.0WarningConfigurableCollection & Type

This rule flags a value in a toJson map that jsonEncode cannot serialize — a DateTime, an enum, or a nested model with no toJson of its own.

jsonEncode accepts only num, String, bool, null, List and Map. Anything else throws JsonUnsupportedObjectError.

The type system does not help here, because Map<String, dynamic> accepts every value. The mistake compiles cleanly and only fails when the map is actually encoded — often in a different layer, on a code path that tests do not cover. What should have been a type error becomes a production stack trace.

See also: dart:convert jsonEncode, JsonUnsupportedObjectError

class Event {
final DateTime createdAt;
final Status status;
Map<String, dynamic> toJson() => {
'createdAt': createdAt, // throws at encode time
'status': status, // enums are not encodable either
};
}

Convert each value to something jsonEncode understands:

Map<String, dynamic> toJson() => {
'createdAt': createdAt.toIso8601String(),
'status': status.name,
};

A nested model is fine as long as it declares its own toJsonjsonEncode reaches it through the toEncodable hook:

class Address {
Map<String, dynamic> toJson() => {'city': city};
}
Map<String, dynamic> toJson() => {'address': address}; // accepted

Collections are checked through their type arguments, so List<DateTime> is reported while List<String> is not. A Map’s values are checked; its keys are not — that is a blind spot in this rule, not a safe case. jsonEncode does not stringify non-String keys, it throws: both jsonEncode({1: 'x'}) and jsonEncode({DateTime(2020): 'x'}) raise JsonUnsupportedObjectError. Only Map<String, …> encodes.

dynamic and Object values are never reported — the runtime value may well be encodable, so any report would be guesswork. Type parameters are skipped for the same reason.

Only map literals returned directly from toJson are inspected. A map built up statement by statement, or returned from a helper, is not analysed.

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

To turn it off:

many_lints.yaml
rules:
avoid_not_encodable_in_to_json: false

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

analysis_options.yaml
many_lints:
rules:
avoid_not_encodable_in_to_json:
allowed_types: [Decimal, Uint8List]
Option Type Default Description
allowed_types list of strings [] Type names to treat as encodable, for projects whose serializer handles them through a custom converter