DOTNET Contents

Validation Errors as Problem Details

Validation errors must be predictable and machine-readable. Learn how to return Problem Details for invalid input with stable field errors, consistent status codes, and zero information leaks.

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.

Status Code Choice: 400 vs 422

Both can be defensible, but inconsistency is not. Recommendation for most APIs: - Use 400 for invalid input. - Keep 422 only if you have a very clear semantic split and it is documented. Production rule: Pick one strategy, apply it everywhere.

Do Not Leak Rule Implementation Details

Validator messages may reveal too much: - exact constraints that help attackers optimize payloads - internal model names - business logic details Balance: - enough info for legitimate client correction - not enough to help abuse Production rule: Use stable, safe messages. Avoid echoing raw input in errors.

Field Naming Consistency

A classic production pain: - C# property names are PascalCase - JSON uses camelCase - clients map fields by JSON names Ensure your error keys match your JSON contract: - quantity, sku, shippingAddress.postalCode (if you use dotted paths) If you change naming policy, you must update validation errors too, or clients break.

Operational Notes

Monitoring: - Track validation failures rate separately from 5xx. - Track top invalid fields (bounded cardinality). - Alert on sudden 400 spikes after deploy (breaking changes or client rollout issue). Rollout: - Validation rule changes can be breaking changes. - Canary rule changes and monitor 400 rates. - Keep backward compatibility where possible. Rollback: - If validation changes block legitimate traffic, rollback fast. - Do not disable validation globally; it becomes a security hole.

Checklist

- Validation errors return Problem Details consistently. - A stable error_code is included (e.g., VALIDATION_FAILED). - Field-level errors are included in a predictable structure. - Field names match the JSON contract used by clients. - Status codes for validation are consistent across endpoints. - Error messages are safe and do not leak internal details. - Validation failure metrics monitored separately from 5xx. - Validation rule changes are canaried and rollbackable.