What should a framework do when a handler declares a parameter the incoming request does not carry?
answer
- absent, empty, and present are three states
- requiredness is declared, not discovered
- missing required query value means 400
- a missing path capture means 404 instead
- never substitute zero for absent
basics
~20 sA missing required value should fail with 400, naming the parameter and its source; a missing optional one should reach the handler as an explicit absence. A path capture cannot be missing: a non-matching template gives 404 instead.
solid answer
~50 sEvery bound parameter carries a **requiredness** decision, either stated in the declaration or taken from a framework default — and defaults genuinely differ: some binders treat an undeclared parameter as required, others as optional. A missing **required** value is a client error: answer **400** with a message naming the parameter *and* the source, because "missing `id`" is ambiguous when the same name could come from three places. A missing **optional** value must reach the handler as an explicit absence, not as a silently substituted zero or empty string, which is the mistake that turns a bad request into wrong data. **Path** captures are the exception: if a segment were absent the route would not have matched, so the request ends as **404** at routing and never reaches binding. Missing and empty are different states — a query string ending in `q=` supplies the name with an empty value.
go deeper
Remember that a missing required value is the client's error and belongs with 400, and that a value the client sent as empty is not the same as one they never sent.
Explain where requiredness is declared, why the framework default matters, and why a missing path capture ends as 404 at routing rather than 400 at binding.
Talk about error shape under load: naming the source as well as the parameter, gathering all failures in one response, and never letting a substituted value travel past the boundary.
Make requiredness an explicit, reviewed part of every endpoint's contract, and keep the handler's declaration and the published description from drifting apart in either direction.
## Three states, not two A parameter the handler declares can be in one of three states on any given request, and conflating them is the source of most binding bugs: 1. **Absent** — the name does not appear in the source at all. 2. **Present and empty** — the name appears with no value: a query string ending in `q=`, a form field submitted blank, a header sent with an empty value. 3. **Present with a value.** A binder that maps state 2 onto state 1, or either onto a substituted zero, hands the handler a value the client never sent. The handler then cannot tell "the user cleared the search box" from "the user never opened it" — a distinction that decides whether a stored filter is reset or left alone. ## Requiredness is a declaration Whether absence is an error is part of the parameter's declaration, not a property of the request. The declaration may be explicit, or it may come from a framework-level default — and those defaults differ: some binders treat an undeclared parameter as required and fail the request when it is absent, while others treat absence as an ordinary optional value and pass nothing. Some derive requiredness from the parameter's type, where a type that admits absence signals optional and one that does not signals required. The consequence of not knowing which convention is in force is that an endpoint's tolerance for a missing value is accidental: nobody chose it, and it can change when a default changes. ## What each source does when a value is missing | Source | Missing means | Typical outcome | |---|---|---| | Path capture | the template did not match | 404 at routing, binding never runs | | Query | the client omitted the name | 400 if required, absence if optional | | Header | the client sent no such header | 400 if required, absence if optional | | Cookie | not in the cookie header | 400 if required, absence if optional | | Form field | the encoded body has no such key | 400 if required, absence if optional | The **path** row is the one worth saying out loud in an interview. A path capture is required by construction: it participated in the routing decision, so a request that omits it never reaches this handler. Answering "400 for a missing path variable" reveals a model in which routing and binding are the same step, which they are not — and the practical consequence is real, since a mistyped identifier in a path is a 404 while a mistyped query key is a 400 or a silent absence. ## What a good failure looks like When a required value is missing, the response should: - carry **400**, because the client can fix the request; - name the **parameter** and its **source** — "missing required query parameter `tenant`" rather than "missing `tenant`", since the same name might legitimately exist in a header; - report **all** missing parameters where the framework can gather them, so a client fixing a form does not discover the problems one round trip at a time; - avoid echoing untrusted input back into the message body unescaped, and avoid leaking internal field names that differ from the wire contract. Authentication is the deliberate exception: an absent credential header is normally answered by the security layer with **401** and a challenge, not by the binder with 400, because it is not a malformed request but an unauthenticated one. ## The mistakes this prevents - **Silent substitution.** Letting an absent numeric parameter become `0` turns a missing page number into page zero, and an absent amount into a free transaction. - **Absence as emptiness.** Treating an absent filter and an empty filter alike produces "clear" and "do nothing" behaving identically, which users notice long before tests do. - **Optional by accident.** A parameter every real client happens to send behaves as if required until the one client that omits it arrives, at which point the handler takes a path nobody tested. - **Required by accident.** A parameter added as optional in the API description but declared required in the handler rejects requests that the published contract says are valid. The discipline is small: decide requiredness deliberately for each parameter, keep absence distinguishable from emptiness all the way into the handler, and let the boundary reject rather than let a substituted value travel inward.
- Why is a missing path variable a 404 rather than a 400?Because it is a routing outcome, not a binding one. The capture is part of the pattern the router matches; if the segment is not there, no route matches and the server answers 404 before any handler is chosen. Binding only ever runs for a request that already satisfied the template, so it never sees a missing path value to complain about.
- Should an absent authentication credential be a 400 from the binder?No. A missing credential is an authentication outcome: the security layer answers 401 with a challenge telling the client how to authenticate. Binding the credential header as a required parameter would produce 400 instead, which is the wrong signal and skips the challenge, so credentials are normally handled before binding rather than as an ordinary parameter.
- How do you keep "absent" distinguishable from "empty" once the value is inside the handler?Bind to a representation that can hold absence — a type or wrapper with a distinct no-value state — and resist collapsing it early. The moment absence is normalised to an empty string or a zero, the distinction is gone and no later layer can recover it, so any behaviour that depends on it has to be decided at the binding boundary.
saying these in an interview costs you the question
- Answering 400 for a missing path variable instead of 404
- Treating an absent parameter and an empty value as the same thing
- Letting a missing number bind as zero rather than failing
- Assuming every framework defaults parameters to optional
- Reporting one missing field per response when several are missing