Why does a dataclass with a defaulted field before a non-defaulted one raise TypeError?
answer
- Fields become constructor parameters, in order
- Python's own signature rule
- Raised while the decorator runs
- Base-class fields come first
- non-default argument follows default argument
basics
~10 sThe generated init takes fields in annotation order, so a defaulted field followed by a non-defaulted one would produce an invalid signature. The decorator raises TypeError at class-creation time: non-default argument follows default argument.
solid answer
~50 s`@dataclass` builds `__init__` with one parameter per field, in the order the annotations appear, using each field's default as the parameter default. Python's own grammar forbids a parameter without a default after one with a default, so a class body that annotates `name: str = "unknown"` and then `employee_id: int` cannot produce a legal signature. The decorator detects this while it runs and raises `TypeError: non-default argument 'employee_id' follows default argument 'name'` — at import time, not at first use. The fixes are to move the non-defaulted field above the defaulted one, to give it a default (often `None` plus a check), or to make the later fields keyword-only so ordering no longer constrains them. The same rule bites across inheritance: base-class fields come first, so once a base class ends in a defaulted field, every field a subclass adds positionally must have a default too.
code
python · 10 linesfrom dataclasses import dataclass
try:
@dataclass
class Row:
name: str = "unknown"
employee_id: int # no default after a defaulted field
except TypeError as exc:
print(exc)
# non-default argument 'employee_id' follows default argument 'name'go deeper
Recognise the message 'non-default argument follows default argument' and know the immediate fix: put fields without defaults before fields with defaults in the class body.
Explain the cause — fields become init parameters in annotation order, and Python forbids a non-default parameter after a default one — and that the TypeError is raised while the decorator runs, at import time.
Show that you have hit the inheritance form: a base dataclass that gains a defaulted field forces defaults on every subclass field, so adding one is a breaking change across the subtree.
Own field order as a public contract for a shared value type: positional call sites, subclass freedom, and when to mandate keyword-only construction across a codebase to decouple base classes from their subclasses.
## The rule is Python's, not the decorator's A Python function signature cannot have a parameter without a default after one with a default — there would be no way to supply the later argument positionally without also supplying the earlier one. `def f(a=1, b)` is a `SyntaxError` in any Python file. `@dataclass` generates `__init__` with one parameter per field, in annotation order, using each field's class-body value as that parameter's default. So the field order in the class body *is* the parameter order of the constructor, and the same rule applies: ```python from dataclasses import dataclass @dataclass class Row: name: str = "unknown" employee_id: int # no default, after a defaulted field # TypeError: non-default argument 'employee_id' follows default argument 'name' ``` Two details worth stating precisely in an interview. First, it is a `TypeError`, not a `SyntaxError` — the class body itself is valid Python; the decorator discovers the problem when it assembles the signature. Second, it is raised **at class-creation time**, so the module fails to import. That is the good case: it is impossible to ship this bug. ## The three fixes **Reorder.** Required fields first, defaulted fields after. This is usually the right answer and often improves the class — the fields a caller must supply read as the record's core, the optional ones as trimming. **Give it a default.** Frequently `None` with a narrower annotation and a validity check, when the field is genuinely optional. Do not reach for a mutable default here; that is a separate error. **Make the later fields keyword-only.** Keyword-only parameters have no positional order to violate, so the constraint disappears entirely. This is the escape hatch when the field order is fixed by something else — readability, or a base class you do not control. ## The inheritance case, which is where it actually bites Subclass fields are appended after the base class's fields, in base-class order: ```python @dataclass class Base: a: int = 0 @dataclass class Sub(Base): b: int # TypeError: non-default argument 'b' follows default argument 'a' ``` Nothing is wrong with `Sub` in isolation. The failure is inherited: because `Base` ends in a defaulted field, every positional field any subclass adds must also have a default — forever. This is the version of the error people actually hit, often when a shared base class grows a convenience default and every downstream subclass breaks at import. The practical consequence for design: a base dataclass intended for subclassing should either avoid defaults entirely, or accept that its whole subtree is defaults-only from that point on. If the hierarchy is deep or shared across teams, keyword-only construction removes the coupling. ## A field that opts out of the constructor is exempt A field declared `field(init=False)` is not a constructor parameter, so it has no place in the signature and cannot violate the ordering rule. It may sit anywhere in the class body regardless of defaults, which is occasionally a clean way to keep a derived value next to the fields it is derived from without disturbing the required/optional split. ## The fix that is not a fix Under time pressure the tempting repair is to give the offending field any default at all so the module imports again. If that default is a list, dict or set the decorator refuses it outright; if it is a mutable-but-hashable object it is accepted and quietly shared by every instance. Worse, a default chosen only to satisfy the ordering rule turns a required field into an optional one, so a caller who forgets to pass it now gets a silently wrong record instead of a `TypeError` at the call site. Reorder the fields, or make them keyword-only; invent a default only when the field is genuinely optional. ## Why this shows up in interviews It is a small rule with a large blast radius, and it tests whether a candidate has a mental model of what the decorator generates. Someone who knows that `__init__` is a generated function with an ordinary signature predicts the error and the fixes immediately. Someone who thinks of `@dataclass` as magic guesses — usually by blaming the annotations or the type of the field, neither of which is involved. The deeper takeaway is that field order in a dataclass is a public contract. It sets the positional argument order for every call site, and it sets what a subclass may add. Changing it is a breaking change, in the same way as reordering the parameters of any function. ## Version notes This check has existed since `dataclasses` landed in Python 3.7. Keyword-only fields, which are the general way out of the ordering constraint, arrived in Python 3.10. The error message names both fields on Python 3.14.
- Is this a SyntaxError or a TypeError, and when is it raised?A `TypeError`, raised while the decorator runs — that is, when the class statement executes at import time. The class body itself is perfectly valid Python; the problem only appears when `@dataclass` assembles a constructor signature from the fields and finds a parameter with no default after one with a default. The module fails to import, so the bug cannot reach production.
- How does inheritance change the field order a subclass sees?Fields from base classes come first, in method-resolution order, followed by the fields the subclass adds. So a base class that ends with a defaulted field forces every positional field in every subclass to have a default too. Adding a default to a widely-subclassed base dataclass is therefore a breaking change for its whole subtree.
- Can a field with no default ever legally follow a defaulted one?Yes, if it is not a constructor parameter. A field declared `field(init=False)` never appears in the generated `__init__`, so the ordering rule does not apply to it and it can sit anywhere in the class body. Making later fields keyword-only also lifts the constraint, since keyword-only parameters have no positional ordering to violate.
saying these in an interview costs you the question
- Calling it a SyntaxError in the class body
- Saying the error appears at first instantiation
- Blaming the annotation or the field's type
- Not knowing base-class fields come first
- Fixing it by giving the field a mutable default