ASP.NET - Problem Details and Standardized API Errors in ASP.NET Core

Introduction

When an ASP.NET Core application encounters an error, the API needs to communicate that problem to the client in a clear and consistent way. Simply returning a status code such as 400, 404, or 500 often does not provide enough information for the client application to understand what went wrong.

Problem Details provides a standardized structure for representing HTTP API errors. It allows an API to return information such as the HTTP status code, a short description of the problem, additional details, and a reference identifying the type of problem. ASP.NET Core provides built-in support for generating Problem Details responses through the IProblemDetailsService and AddProblemDetails() configuration. (Microsoft Learn)

What Is Problem Details?

Problem Details is a standardized format for describing errors that occur while processing an HTTP request. Instead of creating a different JSON error structure for every API endpoint, an application can use a common format.

A typical response can look like this:

{
  "type": "https://example.com/errors/invalid-user",
  "title": "Invalid User",
  "status": 400,
  "detail": "The supplied user information is invalid.",
  "instance": "/api/users/25"
}

The important fields have specific purposes.

1. type

The type field identifies the type of problem. It can point to a documentation page explaining the particular error.

For example:

"type": "https://example.com/errors/invalid-user"

A client or developer can use this URL to understand the error in greater detail.

2. title

The title provides a short, human-readable description of the problem.

For example:

"title": "Invalid User"

It should generally remain consistent for the same type of error.

3. status

The status represents the HTTP status code associated with the problem.

For example:

"status": 400

Common values include:

  • 400 for a bad request

  • 401 for an unauthenticated request

  • 403 for a forbidden operation

  • 404 when a resource cannot be found

  • 409 when a request conflicts with the current state

  • 500 for an unexpected server error

4. detail

The detail field provides additional information about the particular occurrence of the problem.

For example:

"detail": "The email address supplied is already registered."

This field should be carefully controlled because exposing internal exception information can create security risks. Microsoft specifically warns against sending sensitive error information to clients. (Microsoft Learn)

5. instance

The instance field can identify the particular request or resource associated with the problem.

For example:

"instance": "/api/customers/125"

This can be useful when diagnosing errors involving a particular resource.

Why Standardized API Errors Are Important

Without a standardized error format, different API endpoints may return completely different responses.

For example, one endpoint might return:

{
  "error": "User not found"
}

Another might return:

{
  "message": "No user exists with the specified ID."
}

A third might return:

{
  "success": false,
  "reason": "Invalid user"
}

This makes client-side development more complicated because the consuming application must understand several different error structures.

With Problem Details, APIs can follow a consistent structure:

{
  "type": "https://example.com/errors/user-not-found",
  "title": "User Not Found",
  "status": 404,
  "detail": "No user exists with ID 125."
}

The frontend, mobile application, or another API client can therefore process errors in a predictable manner.

Configuring Problem Details in ASP.NET Core

ASP.NET Core provides the AddProblemDetails() extension method for registering the default Problem Details service.

A basic configuration is:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

app.MapControllers();

app.Run();

Here, AddProblemDetails() registers the application's IProblemDetailsService. UseExceptionHandler() handles unhandled exceptions, while UseStatusCodePages() can generate Problem Details responses for HTTP errors that otherwise have no response body. (Microsoft Learn)

Problem Details with Minimal APIs

Problem Details can also be used with Minimal APIs.

For example:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

app.MapGet("/users/{id:int}", (int id) =>
{
    if (id <= 0)
    {
        return Results.BadRequest();
    }

    return Results.Ok(new
    {
        Id = id,
        Name = "John"
    });
});

app.Run();

With AddProblemDetails() configured, ASP.NET Core can generate standardized Problem Details responses for error responses that do not already contain a response body. (Microsoft Learn)

Handling Exceptions

An unexpected exception can occur because of a database failure, programming error, unavailable external service, or another unexpected condition.

Instead of returning a stack trace to the client, an application should normally return a controlled error response.

For example, an unexpected server-side exception could result in:

{
  "type": "https://example.com/errors/internal-server-error",
  "title": "An error occurred while processing your request.",
  "status": 500
}

The detailed exception information should remain in server-side logging rather than being exposed to the API consumer. ASP.NET Core's exception-handling middleware can work with the Problem Details service to produce an appropriate error response. (Microsoft Learn)

Handling 404 Errors

Suppose an API contains an endpoint:

GET /api/products/100

but product 100 does not exist.

Instead of returning an empty or inconsistent response, the API can return:

{
  "type": "https://example.com/errors/product-not-found",
  "title": "Product Not Found",
  "status": 404,
  "detail": "The requested product could not be found."
}

This clearly communicates both the HTTP status and the reason for the failure.

ASP.NET Core can also generate Problem Details for routing errors, including requests where no matching endpoint exists. (Microsoft Learn)

Handling Validation Errors

Validation is another important area where standardized errors are useful.

Suppose a registration API expects:

{
  "name": "",
  "email": "invalid-email"
}

The API could return a validation-oriented response explaining that the submitted values are invalid.

ASP.NET Core supports ValidationProblemDetails for validation failures. For controller-based APIs, MVC can automatically produce Problem Details for error results and validation failures. (Microsoft Learn)

A validation response might conceptually look like:

{
  "type": "https://example.com/errors/validation",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "name": [
      "Name is required."
    ],
    "email": [
      "Enter a valid email address."
    ]
  }
}

The errors property makes it possible for a client application to associate individual validation messages with specific fields.

Customizing Problem Details

Applications often need to add information specific to their environment.

ASP.NET Core allows Problem Details to be customized through ProblemDetailsOptions.CustomizeProblemDetails, a custom IProblemDetailsWriter, or the IProblemDetailsService itself. (Microsoft Learn)

For example:

builder.Services.AddProblemDetails(options =>
{
    options.CustomizeProblemDetails = context =>
    {
        context.ProblemDetails.Extensions["application"] =
            "Customer API";
    };
});

This can produce an additional property such as:

{
  "title": "Not Found",
  "status": 404,
  "application": "Customer API"
}

Custom properties can be useful for adding controlled diagnostic information.

Adding Extensions

Problem Details supports additional information through extension properties.

For example:

{
  "type": "https://example.com/errors/payment-failed",
  "title": "Payment Failed",
  "status": 400,
  "detail": "The payment could not be completed.",
  "errorCode": "PAYMENT_001"
}

Here, errorCode is an application-specific property.

This approach is useful when a frontend needs a stable code to determine what action should be taken.

For example:

PAYMENT_001
ACCOUNT_LOCKED
PRODUCT_UNAVAILABLE
INVALID_COUPON

The application can use these codes without having to interpret human-readable error messages.

IProblemDetailsService

IProblemDetailsService is the central service provided by ASP.NET Core for creating Problem Details responses.

It can be used when an application needs more control over how an error response is generated.

For example:

var problemDetailsService =
    context.RequestServices.GetRequiredService<IProblemDetailsService>();

The service can then be used to write a Problem Details response.

ASP.NET Core also provides TryWriteAsync() for situations where the application wants to determine whether a Problem Details response could be generated. (Microsoft Learn)

ProblemDetails Versus Exception Handling

Problem Details and exception handling serve different purposes.

Exception handling determines what happens when an unexpected exception occurs.

Problem Details determines how the resulting HTTP error information can be represented and communicated to the client.

They therefore work well together.

A typical flow is:

Client Request
      |
      v
ASP.NET Core Application
      |
      v
Exception or HTTP Error
      |
      v
Exception Handler / Status Code Middleware
      |
      v
Problem Details Service
      |
      v
Standardized JSON Error Response
      |
      v
Client Application

This separation makes the application's error-handling architecture easier to maintain.

Production and Development Environments

Error responses should also differ between development and production environments.

During development, developers may need detailed exception information to diagnose a problem. ASP.NET Core provides the Developer Exception Page for this purpose.

In production, returning stack traces, database information, file paths, or internal exception messages can expose sensitive information. Microsoft recommends avoiding the disclosure of sensitive error information to clients. (Microsoft Learn)

A production API should therefore return a controlled response such as:

{
  "title": "An error occurred while processing your request.",
  "status": 500
}

while the complete technical information is recorded in server-side logs.

Advantages of Problem Details

The main advantages include:

  1. Consistency
    All API errors can follow a common structure.

  2. Better client-side handling
    Frontend and mobile applications can process errors predictably.

  3. Improved debugging
    Fields such as type, detail, and custom error codes can provide useful diagnostic information.

  4. Clear separation of concerns
    Error generation and error presentation can be handled independently.

  5. Better API documentation
    Standardized error structures make it easier to document possible API failures.

  6. Reduced custom error-handling code
    ASP.NET Core provides built-in support instead of requiring every application to create its own error-response framework.

  7. Security control
    Applications can provide useful information to clients without exposing internal exception details.

Best Practices

When implementing Problem Details in ASP.NET Core, developers should follow several practices.

First, use a consistent error structure across the API rather than creating a different JSON format for every endpoint.

Second, use appropriate HTTP status codes. A validation problem should generally not be represented as a successful 200 response simply because the server technically processed the request.

Third, avoid returning stack traces, database exception messages, connection strings, file paths, or other internal information to clients.

Fourth, use stable application-specific error codes when clients need to distinguish between different business errors.

Fifth, keep the detail message useful but safe. It should help the API consumer understand the problem without revealing sensitive implementation details.

Finally, log the complete technical error on the server while returning an appropriate Problem Details response to the client.

Conclusion

Problem Details provides a structured and consistent way to communicate API errors in ASP.NET Core. Instead of returning unrelated error formats from different endpoints, applications can provide standardized information such as the error type, title, HTTP status, details, and additional application-specific properties.

ASP.NET Core includes built-in support through AddProblemDetails(), IProblemDetailsService, exception handling, status-code middleware, and customization mechanisms. (Microsoft Learn)

For modern ASP.NET Core applications, this approach is particularly valuable because APIs are frequently consumed by web applications, mobile applications, microservices, and third-party clients. A predictable error format allows all these clients to understand failures consistently while keeping sensitive server-side diagnostic information protected.