Skip to content

avoid_not_encodable_in_to_json

v1.0.0WarningConfigurableCollection & Type

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. The diagnostic names the offending type.

jsonEncode accepts only num, String, bool, null, List and Map; anything else throws JsonUnsupportedObjectError. The type system does not help, because Map<String, dynamic> accepts every value — so the mistake compiles cleanly and fails only when the map is actually encoded, usually in another layer on a path the tests do not cover.

enum Status { draft, published }
class Event {
const Event(this.createdAt, this.status);
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:

enum Status { draft, published }
class Event {
const Event(this.createdAt, this.status);
final DateTime createdAt;
final Status status;
Map<String, dynamic> toJson() => {
'createdAt': createdAt.toIso8601String(),
'status': status.name,
};
}

jsonEncode reaches a nested object through its toEncodable hook, so a model that declares toJson is accepted as a value:

class Address {
const Address(this.city);
final String city;
Map<String, dynamic> toJson() => {'city': city};
}
class Order {
const Order(this.address);
final Address address;
// Accepted — Address declares toJson
Map<String, dynamic> toJson() => {'address': address};
}

A model without one is reported, which is the useful part: it catches the class you forgot to give a toJson before the encode does.

Collections are checked through their element type

Section titled “Collections are checked through their element type”
// Don't — List<DateTime> is reported
Map<String, dynamic> toJson() => {'reminders': reminders};
// Do
Map<String, dynamic> toJson() => {
'reminders': [for (final r in reminders) r.toIso8601String()],
};

A project with a JsonConverter for a value type can name it, so the rule stops reporting what the serializer already knows how to write:

many_lints.yaml
rules:
avoid_not_encodable_in_to_json:
allowed_types: [Decimal, Uint8List]
// Accepted with the config above
Map<String, dynamic> toJson() => {'amount': amount}; // amount is a Decimal

See also: dart:convert jsonEncode, JsonUnsupportedObjectError

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