dataclasses: order=True Crashes Only When an Early Field Ties (tested)

@dataclass(order=True) generates __lt__/__le__/etc. by comparing each instance's fields as a tuple, in field-declaration order. We measured what happens when a later field in that tuple -- a dict, which has no ordering defined -- is only ever reached when an earlier field happens to be equal between two instances.

1. Sorting works fine as long as the first field is always different

from dataclasses import dataclass, field

@dataclass(order=True)
class Task:
    priority: int
    config: dict = field(default_factory=dict)

tasks = [Task(3, {"a": 1}), Task(1, {"b": 2}), Task(2, {"c": 3})]
print(sorted(tasks))
[Task(priority=1, config={'b': 2}), Task(priority=2, config={'c': 3}), Task(priority=3, config={'a': 1})]

Measured: sorting works cleanly. Tuple comparison short-circuits on the first field that differs -- since every priority value here is unique, the generated __lt__ never needs to look at config at all to decide the order.

2. The exact same code crashes the moment two priorities tie

from dataclasses import dataclass, field

@dataclass(order=True)
class Task:
    priority: int
    config: dict = field(default_factory=dict)

tasks = [Task(3, {"a": 1}), Task(1, {"b": 2}), Task(1, {"c": 3})]
sorted(tasks)
TypeError: '<' not supported between instances of 'dict' and 'dict'

Measured: with two Task objects sharing priority=1, tuple comparison can't decide from the first field alone, so it falls through to compare config -- and dict has no < operator, so sorted() raises TypeError. The class, the data shape, and the calling code are all identical to the working example; only whether two priorities happen to collide determines whether this crashes. Test data with distinct priorities will pass every time; real data with a duplicate priority value fails the first time it occurs, often far from wherever the objects were created.

3. The fix: mark trailing non-orderable fields compare=False

from dataclasses import dataclass, field

@dataclass(order=True)
class Task:
    priority: int
    config: dict = field(default_factory=dict, compare=False)

tasks = [Task(3, {"a": 1}), Task(1, {"b": 2}), Task(1, {"c": 3})]
print(sorted(tasks))
[Task(priority=1, config={'b': 2}), Task(priority=1, config={'c': 3}), Task(priority=3, config={'a': 1})]

Measured: setting compare=False on config removes it from the generated comparison methods entirely, so tied priorities simply keep their relative input order (Python's sort is stable) instead of ever touching the unorderable field. If an order=True dataclass has any field after the first that isn't itself always orderable, mark it compare=False unless you genuinely want it to participate in tie-breaking.

Frequently asked questions

Why does sorting a list of @dataclass(order=True) objects sometimes raise TypeError and sometimes not?

Because the generated comparison methods compare fields as a tuple in declaration order, short-circuiting on the first field that differs -- we measured sorting working fine when a leading int field was always unique, then raising TypeError the moment two objects tied on that field and comparison fell through to an unorderable dict field.

Does @dataclass(order=True) check that all fields are orderable when the class is defined?

No -- we measured a dataclass with a dict field (which has no __lt__) defining successfully under order=True, with no error until two instances actually tied on an earlier field during a real comparison at runtime.

How do I prevent a trailing unorderable field from breaking sort on an order=True dataclass?

Mark it field(compare=False) -- we confirmed this excludes the field from the generated comparison methods entirely, so tied leading fields fall back to Python's stable sort order instead of attempting to compare the excluded field.

Is it safe to test sorting on an order=True dataclass with unique field values and assume it always works?

No -- we measured identical code and class definitions working with unique leading-field values and crashing with duplicate ones, since the crash only happens when comparison falls through to a later, unorderable field. Test data should include tied values on every leading field.

Does Python's sorted() guarantee stable order for equal elements?

Yes, per Python's documented sort stability -- we confirmed that once the unorderable field is excluded from comparison via compare=False, objects with equal priority values keep their original relative input order rather than being reordered arbitrarily.

Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in dataclasses module: created @dataclass(order=True) objects with a leading int field and a trailing dict field, then sorted a list where the int field values were all unique, and separately a list where two objects shared the same int value. PlainHeaders is our extension; Python's dataclasses module is built in.