Validation Errors as Problem Details
On this page
Validation Errors Are the Most Common Errors
In a healthy API, 400s happen often: - clients send invalid payloads - clients run older versions - users paste garbage - third parties integrate incorrectly If validation errors are inconsistent, you create: - client-side guesswork - support ticket noise - slow incident triage - accidental information leaks Production rule: Validation failures must return a stable schema that clients can rely on.Real Production Incident
Symptoms: - Web app shows “Unknown error” for invalid inputs because it cannot map server errors to form fields. - Mobile app shows different messages than web for the same error. - After deployment, a spike in 400s causes alarm because the team cannot distinguish “expected validation errors” from real issues. Root cause: - Validation errors were returned as ad-hoc strings. - Field-level errors were not included consistently. - Some endpoints returned 400, others returned 422 with different formats. This is an error contract failure.Symptom → Cause → Diagnosis → Fix
Symptom: - client cannot highlight invalid fields reliably - inconsistent error formats per endpoint - confusion between expected 400s and production incidents Cause: - no standard validation error schema - mixing validation mechanisms (ModelState, FluentValidation, ad-hoc checks) - inconsistent status codes for validation failures - returning raw validator messages that change over time Diagnosis: - Submit the same invalid payload to multiple endpoints and compare responses. - Inspect whether errors contain field names and machine-readable codes. - Check whether status codes are consistent. - Audit whether error responses leak internal rule details. Fix: - Standardize validation errors using Problem Details. - Include a stable structure for field errors. - Choose a single status code strategy (usually 400) and enforce it. - Avoid exposing internal rule implementation details.What Clients Need From Validation Errors
A production-grade validation error should provide: - stable top-level error_code - a list/map of field errors - optional per-field error codes (stable identifiers) - correlation id for debugging Clients should not parse error message strings.Anti-Pattern: String-Based Validation Errors
This breaks clients and drifts:
return Results.BadRequest("Quantity must be greater than zero");
Or:
return Results.BadRequest(new { error = "invalid input" });
No field mapping. No stable codes. No contract.
Correct Pattern: Problem Details With Field Errors
Use a stable schema. Example: - status: 400 - type: https://example.com/problems/validation - error_code: VALIDATION_FAILED - errors: map of field -> list of messages/codes Example response construction (conceptual):
var errors = new Dictionary<string, string[]>
{
["quantity"] = new[] { "Must be greater than 0" },
["sku"] = new[] { "Required" }
};
return Results.Problem(
title: "Validation failed",
statusCode: 400,
type: "https://example.com/problems/validation",
detail: "One or more fields are invalid.",
extensions: new Dictionary<string, object?>
{
["error_code"] = "VALIDATION_FAILED",
["errors"] = errors,
["correlation_id"] = http.TraceIdentifier
});
Production rule:
errors must reference stable field names used by clients.