In a web framework, when a bound request model fails its declared constraints, what does the validation step hand back?
answer
- a set, not a single error
- each violation names a location
- rejected value rides along
- message key plus parameters
- no status, no wire names yet
basics
~20 sA collection of violation records, not one error. Each carries the property path that failed, the value that was rejected, the identity of the constraint that rejected it, and a message key with its parameters.
solid answer
~40 sMost validation engines check every declared constraint on the bound model and accumulate the failures, so the hook that runs next receives a collection rather than one exception per field. A violation typically carries four things: a **property path** such as `items[3].quantity`, the **rejected value**, the **identity of the rule** that failed (a length rule, a required rule, a pattern), and a **message key plus parameters** like `min=1` and `max=50` rather than a finished sentence. Order is often unspecified, so anything rendering the set should sort by path if the output has to be stable. Nothing in the set is HTTP-shaped yet: no status, no wire field names, no final text.
go deeper
Recall the vocabulary: validation yields a collection, and each violation names a path, a rejected value, a rule and a message key. Every later discussion about error bodies stands on those four parts.
Explain the accumulation mechanics — the engine walks declared constraints and gathers failures instead of throwing per property — and say what is still missing afterwards: status, wire names and final text.
Show judgment about the rejected value. It is untrusted input, so decide per field whether it is logged, redacted or dropped before anything it touches reaches a response.
Treat the violation collection as an internal contract between the validation engine and one converter. Keeping handlers away from that structure is what makes the wire shape changeable later in one place.
## What the validation step actually produces A server-side web framework handles an incoming body in two moves. First it **binds**: the raw bytes are deserialized onto a typed input model. Then it **validates**: the constraints declared on that model — required, length, range, pattern, a custom rule — are checked against the values that were just bound. The second move does not normally throw on the first bad property. Most validation engines walk the whole model, accumulate every failure they find, and hand back a **collection of violation records**. That collection is the raw material for everything downstream: the error body, the log line, the metric. Nothing in it is HTTP-shaped yet. ## Anatomy of one violation | Element | What it holds | Why the converter needs it | |---|---|---| | Property path | Where the failure sits in the model, e.g. `items[3].quantity` | Lets a caller point at the exact input control | | Rejected value | The value that was actually bound | Debugging and logs — but it is raw client input | | Constraint identity | Which rule failed: a size rule, a required rule, a pattern | The stable machine code a client can branch on | | Message key + parameters | A template reference plus `min`, `max`, the pattern, the actual length | Lets one place render text, in any language | | Root model type | The model the constraints were declared on | Grouping, and mapping model names to wire names | Two of these are commonly misread. The **message** is usually a *key or template plus parameters*, not a finished sentence — some engines do eagerly interpolate a default-language string, but the parameters are still the part that survives translation. And the **rejected value** is whatever the client sent, so it may be a password, a token or a personal identifier; recording it is not the same as publishing it. ## Why a collection rather than one failure at a time - A form with eight bad fields is fixed in **one round trip** instead of eight. - The server pays for binding and rule evaluation **once**, not once per correction cycle. - The caller can highlight everything at once, which is the entire point of a field-level error body. Two honest qualifications. Many engines expose a **fail-fast switch** that aborts at the first violation, usually to save work on large payloads; the shape handed back is still the same collection, holding one element, so nothing downstream changes. And the order of that collection is frequently **unspecified** — it follows however the engine enumerated the model's properties. Anything that renders it should sort by path, or a test that asserts "the first error is the email one" will be flaky for reasons that have nothing to do with validation. ## What the set is not 1. **Not a status code.** Which 4xx code a failed body deserves is an API-design decision made elsewhere; the violation carries no opinion about it. 2. **Not a response body.** The field names in the path are *model* property names. If serialization renames properties on the wire, the path has to be translated before a caller can use it. 3. **Not safe by default.** A message template interpolated with the rejected value can echo the input straight back into the response. ## Turning it into something useful The usual arrangement: the framework's validation hook catches the failure before the handler body runs, and one registered component receives the collection and produces the error body — sorting it, translating paths to wire names, resolving message keys, and applying a redaction policy to rejected values. In that arrangement handlers do not touch violations at all, which is why the shape stays identical across endpoints. ## What weak answers miss The common junior answer is "validation throws an exception and I catch it and return 400". That is not wrong about the control flow, but it skips the shape of what was thrown. The interviewer is looking for awareness that the failure is **structured data** — a set of records, each naming a location, a rule and a parameterised message — because everything else (localization, field-level client highlighting, stable error codes, per-field metrics) is only possible if that structure was preserved rather than flattened into a sentence at the first opportunity.
- Does the validation step ever stop at the first failing property?Yes. Many engines expose a fail-fast mode that aborts once one constraint fails, and some failures abort inherently — a value that cannot even be converted to the property's type stops that branch. The result is still the same collection shape, holding fewer elements, so the code that consumes it does not change.
- Is the rejected value safe to copy into the response body?Not always. It is raw client input, so echoing it puts whatever was sent into a response that logs, proxies and browsers can see. For credential, token or personal fields most teams keep the value server-side for diagnostics and redact or omit it in the body.
- Why carry a message key and parameters instead of the final text?Because the text depends on a locale and on the rule's own numbers. Keeping the key plus `min`, `max` or the actual length lets one component render wording later, or pass the code and parameters through so the client can render its own phrasing consistently.
saying these in an interview costs you the question
- Thinks a framework can report only one failing field per request
- Expects each violation to arrive as finished text in the caller's language
- Assumes the property path already matches the field names on the wire
- Copies the rejected value into the response without thinking about credentials
- Relies on the violation set arriving in declaration order