What should a web framework do with an empty request body, and with extra bytes after a complete parsed value?
answer
- absent is not the same as null
- zero-length still has a media type
- strict fails, lenient binds absent
- one value per body, reject the rest
- disagreement about the message means reject
basics
~20 sTreat an empty body as absent rather than as a null value, and let the handler's declaration decide whether absence is an error. Reject trailing bytes after a complete value: leftover data means the two parties disagree about the message.
solid answer
~40 sThree situations look alike and are not: no body at all, a zero-length body under a declared media type, and a body holding an explicit null literal. A strict parser fails the empty cases with a `400`; a lenient one binds an absent value and lets the argument's own optionality decide. Pick one and apply it everywhere, because the dangerous outcome is a *required* argument that silently becomes a default and turns a client bug into wrong stored data. Trailing bytes after a complete value should be rejected: a parser that stops at the first value silently ignores a truncated retry, a doubled payload or appended junk. Note also that `415` and an empty-body error are different failures — a wrong label versus missing content — and should not share one status.
go deeper
Know that an empty body and a body containing null are different, and that a missing required body is a client error reported in the 4xx range rather than a server error.
Explain the strict and lenient options for an empty body, why consistency matters more than which one you pick, and why a parser should reject bytes after a complete value.
Show the incident angle: a lenient parser defaulting a required field wrote wrong data, or appended bytes in a retried body went unnoticed. Describe the error reporting you added afterwards.
Set the policy once for the platform — strictness, how optionality is declared, and how the four rejection causes are distinguished — so client teams see one predictable contract across every service.
## Four states that all look like nothing "Empty body" is not one condition. A framework can face any of these, and conflating them is how a client bug becomes silent data loss: | State | What was sent | Reasonable outcome | |---|---|---| | No body at all | no body bytes, typically no content type either | the argument is absent; an optional argument binds, a required one fails | | Zero-length body with a media type | a content type but no bytes | the parser has nothing to parse — normally a parse failure | | Whitespace only | bytes that contain no value | same as zero-length for structured formats | | An explicit null literal | a complete, valid value that happens to be null | binds a null value, which is *not* the same as absent | The last row is the one people collapse. A body that explicitly says null is a value the client chose to send; a missing body is a client that sent nothing. If both bind the same way, an endpoint can no longer distinguish "clear this field" from "I did not mention this field", which is exactly the distinction a partial-update endpoint depends on. ## Strict or lenient — pick one and mean it Frameworks differ, and both behaviours are defensible in isolation: - **Strict**: an empty body where a value is expected is a parse failure, mapped to **400 Bad Request**. - **Lenient**: the value binds as absent, and the argument's own declaration — optional, defaulted, nullable — decides whether that is acceptable. What is not defensible is applying them inconsistently across a service. The failure mode is specific and expensive: an argument the handler believes is required binds to a default because a parser was lenient, the handler proceeds, and a request that should have been rejected writes plausible-looking wrong data. Whichever policy you adopt, the check "is this value genuinely required?" must exist somewhere explicit — in the binding declaration or in validation — rather than being an emergent property of the parser's mood. Two adjacent points are worth stating because they are commonly muddled: - A request with no body but a route that requires one is a **client** error, so it belongs in the 4xx range, not 5xx. Letting a parse exception escape as a server error misattributes the fault and pollutes error budgets. - A zero-length body still has a media type if the client sent one, and that type is still looked up. So "unsupported media type" and "body was empty" are **different** rejections with different causes — the first is a wrong label, the second is missing content — and reporting both as one status makes them indistinguishable in logs. ## Trailing data after a complete value Most structured text formats define one value per body. A parser that has read a complete value and finds more non-whitespace bytes has two options: 1. **Reject.** The message is not what its format allows; fail with a 400 naming the offset. 2. **Stop at the first value and ignore the rest.** Convenient, and quietly dangerous. Leniency here hides real problems: - a client that **retried inside the same body** and appended a second copy of the payload; - a **truncated-then-resumed** write producing a valid prefix followed by fragments; - an intermediary or buggy serializer **appending** stray bytes; - deliberately appended content that a downstream component might parse differently from yours. In every case the sender and the receiver end up with different beliefs about what the message was, and no error was raised to tell anyone. The general rule is worth keeping: **when two parties can disagree about what a message contained, reject rather than choose.** ## Making the behaviour legible - Report parse failures with the **position** at which parsing gave up, and say whether that position is a byte or character offset; "invalid body" alone costs the client an afternoon. - Distinguish, in the response and in logs, between *unsupported type*, *empty body*, *malformed body* and *trailing data* — they have four different fixes. - Do not repair bodies. Inserting a default for an empty body, or discarding trailing bytes, moves the client's bug into your data. - Make optionality explicit at the handler boundary, so a reader can tell whether an absent body is legal without knowing the parser's configuration. - Test the three empty states separately. A suite that only covers a well-formed body and a malformed one never touches the case that actually ships broken.
- Why is an explicit null literal not the same as a missing body?A null is a complete value the client deliberately sent; a missing body means the client sent nothing. Endpoints that apply partial updates depend on that difference to distinguish 'clear this field' from 'leave it alone'. Collapsing the two removes a distinction the API cannot recover later.
- What is the practical danger of a lenient parser on a required argument?The argument binds to a default rather than failing, the handler runs as if the client supplied it, and a request that should have been rejected writes plausible wrong data. Nothing errors, so the bug is found downstream in the data rather than at the boundary where it was cheap to fix.
- Should an unsupported media type and an empty body report the same error?No. They have different causes and different fixes: one is a wrong label on a body that may be perfectly fine, the other is a correctly labelled request with no content. Reporting them identically makes them indistinguishable in logs and sends the client looking in the wrong place.
saying these in an interview costs you the question
- Treats a missing body and an explicit null literal as the same thing
- Ignores bytes left over after the first complete value
- Lets an empty-body parse failure surface as a 500
- Inserts defaults for an empty body so the handler always has something
- Reports unsupported type and empty body with the same status