Diagnosing MissingRequiredRequestParameterException in Spring MVC
A Spring MVC endpoint returns HTTP 400 when a required @RequestParam is missing. Learn how to trace the cause to a name mismatch, missing default value, or binding conflict.
15 Nov 2025, 08:14 UTC

When a Spring MVC controller returns HTTP 400 Bad Request because a client left out a query parameter, the request is rejected before your handler method executes. The useful takeaway: the root cause is typically a precise name mismatch, a missing defaultValue, or a binding conflict with another annotation—not a bug in your business logic.
Recognizable condition
You see 400 Bad Request on requests that omit a specific query parameter, while the same endpoint succeeds when the parameter is present. Server logs or the response body reference MissingRequiredRequestParameterException. The failure is deterministic: it happens every time the parameter is absent.
Cause and diagnostic table
Spring uses a HandlerMethodArgumentResolver—the component that maps incoming request data to method arguments—to populate parameters annotated with @RequestParam. When that resolver cannot find a required parameter in the request, it throws MissingRequiredRequestParameterException and the framework translates it into a 400 response.
| Observation | Likely cause | What to inspect |
|---|---|---|
| 400 only when the parameter is omitted | @RequestParam is required and has no defaultValue | Method signature and annotation attributes |
| 400 even when the client appears to send the parameter | Name or case mismatch between request and method | Exact string comparison, including case |
| 400 persists after making the parameter optional | Binding conflict with @ModelAttribute or @PathVariable using the same name | All method parameters and model object fields |
Ordered checks and fixes
Check 1: Verify the parameter name matches exactly
Spring query parameter names are case-sensitive. If your method expects customerId but the client sends customerid, the resolver treats the required parameter as missing.
@GetMapping("/orders")
public List<Order> getOrders(@RequestParam("customerId") String customerId) {
// ...
}
Fix: Align the string inside @RequestParam with the exact name the client sends, or update the client to match the server. If you rely on Java parameter-name reflection, confirm that your build retains parameter names (compiled with -parameters); otherwise the explicit name in the annotation is authoritative.
Check 2: Inspect required and defaultValue attributes
By default, @RequestParam is required. Adding required = false makes it optional, but a missing value then arrives as null for object types. For primitives such as int or boolean, a missing value still causes a conversion error unless you supply a defaultValue.
@GetMapping("/search")
public List<Product> search(
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "sort", required = false) String sort) {
// ...
}
Fix: If the parameter is genuinely optional, prefer defaultValue for primitives and handle null explicitly for object types. If the parameter must be present, remove required = false and any defaultValue so the 400 correctly signals a client error.
Caution: Setting required = false without a defaultValue can push a null into your service layer and produce a NullPointerException later.
Check 3: Rule out binding conflicts
If another method argument consumes the same name, Spring may bind the value elsewhere and leave the @RequestParam unresolved. A @ModelAttribute object with a field named status, or a @PathVariable named status, can intercept the value before the resolver assigns it to your @RequestParam.
@PostMapping("/submit")
public String submit(
@ModelAttribute OrderForm form,
@RequestParam("status") String status) {
// If OrderForm also has a 'status' field, the value may bind there instead.
// ...
}
Fix: Rename the query parameter, rename the conflicting model field, or remove the redundant binding so only one argument claims the name.
Reproduce and verify the fix
Run these commands from any terminal or HTTP client. No special permissions are required; substitute your actual host, port, and path.
# Expected: HTTP 400 when the required parameter is absent
curl -i "http://localhost:8080/api/orders"
# Expected: HTTP 200 when the parameter is supplied
curl -i "http://localhost:8080/api/orders?customerId=123"
After applying a fix—such as adding defaultValue = "0" or correcting a name—repeat the first request. A 200 OK confirms the resolver now finds or defaults the argument. If the 400 remains, search server logs for MissingRequiredRequestParameterException to confirm the same diagnostic path rather than a different failure such as a validation error.
Escalation criteria
- All three checks pass, yet the 400 persists on a single endpoint.
- Multiple unrelated controllers fail with the same exception after a framework upgrade.
- A custom
HandlerMethodArgumentResolveror third-party starter is registered and may be intercepting the parameter.
In these cases, escalate to a senior developer or the platform team with the controller signature, the exact request URL, and the relevant dependency versions.
Limitations
This guide applies to standard Spring MVC servlet applications using annotation-based controllers. It does not cover Spring WebFlux functional endpoints, reactive argument resolvers, or servlet filters that reject requests before handler mapping. Exception class names and messages are stable across Spring Framework 5.x and 6.x, but always verify the exact type in your project's dependency tree before relying on it in catch blocks or error handlers.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.