In a web framework, how does a body that fails to parse differ from one that parses but breaks declared constraints?
answer
- different stages, different knowledge
- one failure versus a set
- wire position versus property path
- coercion failure precedes the rules
- never echo the reader's message
basics
~20 sA parse failure happens before any model exists, so it yields one failure at a wire position with the reader's own wording. A constraint failure happens after binding, so it yields a whole set of model paths, rule codes and parameters.
solid answer
~40 sReading a body is a pipeline: parse bytes, map onto a typed model, then check declared constraints. A malformed body dies at step one — the reader aborts at the offending token, so you get a single failure located by wire position, not a property path, and its message is internal wording that may quote the input. A constraint failure happens after binding succeeded, so every rule could run and a full set is available. The awkward middle case is **type coercion**: text in a numeric property is a mapping failure, so constraints on that property never run and the caller sees a partial error list. One converter should accept both and emit one shape, with a code distinguishing `malformed` from `invalid`.
go deeper
Learn the order: parse, bind, then check rules. A body that cannot be parsed never reaches the rules, which is why its error looks nothing like a field-by-field list.
Explain what is available at each stage — wire position and one failure early, property paths and a full set later — and why a type-coercion failure belongs to the binding stage, not the rule stage.
Show that you have watched callers fix one field and hit five more. Argue for permissive bind-time types with real rules doing the checking, and for logging reader messages rather than publishing them.
Decide the vocabulary. Distinct codes for malformed and invalid let dashboards separate a broken client release from users typing bad values, which is a different response from a different team.
## Two failures at two stages Reading a request body is a pipeline. The bytes are parsed into a generic structure, that structure is mapped onto a typed model, and only then are the declared constraints evaluated against the bound values. A failure can happen at any of those steps, and **what you know at the moment of failure differs completely** between them. - **Parse failure** — the bytes are not well-formed at all: a truncated payload, a stray comma, a body that is not the media type it claimed. There is no structure and no model. The reader stops at the first offending token, because after that point it cannot know what the rest was meant to be. - **Constraint violation** — the bytes parsed, the model exists, values are bound, and a declared rule rejected one of them. Every rule can be evaluated, so a set of failures is available. ## The comparison that matters | | Parse failure | Constraint violation | |---|---|---| | When | Before a model exists | After binding succeeded | | How many | Normally one — the reader aborts | Potentially all of them at once | | Location expressed as | A wire position, or the token that broke | A model property path | | Message origin | The reader's internal wording | A declared rule's message key and parameters | | Can it be collected | No, the rest of the body is unknown | Yes, that is the usual mode | | Safe to pass through | No — often quotes input or internal type names | Only after a redaction policy | ## The hybrid everyone trips over: type coercion The interesting case sits between the two. The body parses fine, but one value cannot be converted to the property's declared type — text where a number belongs, a malformed date, an unknown enumerated value. The mapper rejects it during binding, **before any constraint on that property has run**. Two consequences follow, and they are what a strong answer names: 1. The failure is a **mapping** failure wearing the clothes of a validation failure. Frameworks vary: some abort binding entirely, some bind the rest of the model and record the bad property, some substitute a default and lose the fact that anything was wrong. 2. If binding aborts, the caller receives an error body describing **one** bad field, submits a fix, and then discovers the other five failures the constraints would have found. The caller sees a "partial" error list and experiences the API as dishonest, through no bug in either side. That is the argument for having the converter accept mapping failures too, and for preferring input types that are permissive at bind time with a declared rule doing the real checking, wherever that is practical. ## Shaping both into one body The caller should not be forced to parse two different error formats for what is, from their side, "my request was not acceptable". So the same central converter takes two kinds of input and emits one shape: 1. For a violation set: paths, rule codes, parameters, sorted. 2. For a parse or coercion failure: a single entry, with a path when the reader can name the property it choked on and a body-level marker when it cannot, and a **code that distinguishes malformed from invalid** so a client can tell "your JSON is broken" from "your quantity is too small". Keeping that distinction visible in the code, rather than flattening both into one generic code, is what lets a client show the right message and lets a dashboard tell a broken client release apart from users typing bad values. ## Never pass the reader's message through Parse and coercion messages are written for the developer of the framework, not for a caller. They routinely include a fragment of the offending input, the internal name of the target type, and sometimes a file or stream position. Forwarding that verbatim leaks implementation detail to anyone who can send a bad body, and reflects attacker-controlled bytes back into a response. Log it with the request identifier; publish a short, fixed sentence plus the stable code. ## The mental model to state in an interview "Parsing tells me whether I have an object at all; constraints tell me whether the object is acceptable. The first fails early and alone, the second fails late and in bulk, and the type-coercion case is a parse failure that people mistake for a constraint failure. My error body has to carry both, distinguish them by code, and never repeat the parser's own words back to the caller." Whether either deserves one status code or another is a separate API-design decision — the mechanism is the same regardless of which is chosen.
- Why does a malformed body usually produce only one error?Because the reader cannot interpret what follows the break. Once a structure is truncated or a token is unexpected, the remaining bytes have no reliable meaning, so continuing would invent failures. Constraint checking is different: the model is complete, so every rule can be evaluated independently.
- Why can a caller fix the reported field and immediately hit five more errors?Because a value that cannot be converted to its property type fails during binding, before constraints run. If binding aborts there, the constraint pass never happens and the response lists only the coercion failure. Fixing it lets the rules run and reveal the rest.
- What should the body say when the reader's own message is detailed and helpful?Say less. Reader messages routinely quote the offending input and internal type names, so publishing them reflects attacker-controlled bytes and leaks implementation detail. Log the full message with a request identifier and return a short fixed sentence plus a stable machine code.
- How should the two cases be told apart by a client?By distinct machine codes rather than by reading prose. A `malformed` code means the request could not be understood at all and usually indicates a broken client build; an `invalid` code means the values were understood and rejected, which the user can fix in the form.
saying these in an interview costs you the question
- Treats an unparseable body as just another field violation
- Expects a full field list when the payload was truncated
- Forwards the reader's raw exception message straight to the caller
- Does not know that coercion failures pre-empt the constraints on that property
- Uses one generic code for both, so clients cannot tell them apart