DEV Community

Cover image for Your API Errors Shouldn't Be a Guessing Game
Mohamed Ismail
Mohamed Ismail

Posted on AI-assisted

Your API Errors Shouldn't Be a Guessing Game

Imagine you're building a frontend application and an API request fails.

The backend returns:

{
  "message": "Something went wrong"
}
Enter fullscreen mode Exit fullscreen mode

Great. But what went wrong?

Was it a validation error? A missing resource? A permission issue? Or something else?

Now the frontend has to guess what happened.

There's a better way to handle API errors, and it's already standardized.

The Problem With API Errors

Different APIs often return errors in different formats.

One endpoint returns:

{
  "error": "Invalid email"
}
Enter fullscreen mode Exit fullscreen mode

Another returns:

{
  "message": "User not found",
  "code": 404
}
Enter fullscreen mode Exit fullscreen mode

And another might accidentally expose an internal database error.

This makes frontend development harder because every endpoint needs different error-handling logic.

What if every API followed the same error structure?

Meet Problem Details

RFC 7807 introduced a standard format called Problem Details for HTTP APIs.

It was later replaced by RFC 9457, which is the current standard.

The idea is simple: return errors in a consistent, predictable format.

Here's an example:

{
  "type": "https://api.example.com/problems/insufficient-funds",
  "title": "Insufficient Funds",
  "status": 422,
  "detail": "Your balance is $40, but the total is $55.",
  "instance": "/checkouts/123"
}
Enter fullscreen mode Exit fullscreen mode

Much better, right?

Every field has a purpose:

  • type: Identifies the kind of error.
  • title: A short description of the problem.
  • status: The HTTP status code.
  • detail: Explains what happened in this request.
  • instance: Identifies the specific occurrence of the problem.

The API should also return the appropriate HTTP status code and this content type:

Content-Type: application/problem+json
Enter fullscreen mode Exit fullscreen mode

Now the frontend knows what kind of problem occurred without guessing from random messages.

What About Form Validation?

This is where it gets even more useful.

Imagine a registration form with invalid inputs.

Your API could return:

{
  "type": "https://api.example.com/problems/validation",
  "title": "Validation Failed",
  "status": 422,
  "errors": [
    {
      "field": "email",
      "message": "Invalid email address"
    },
    {
      "field": "password",
      "message": "Must contain at least 8 characters"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The errors array is a custom extension, not a required field in the standard.

Now the frontend can display each error beside the correct input.

No need to parse random error strings.

Handling Errors in JavaScript

Here's a simple example using fetch:

const response = await fetch("/api/register", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify(formData)
});

if (!response.ok) {
  const problem = await response.json();

  if (problem.type === "https://api.example.com/problems/validation") {
    console.log(problem.errors);
  } else {
    console.error(problem.title);
  }
}
Enter fullscreen mode Exit fullscreen mode

In a real application, you should also handle network failures and responses that don't contain valid JSON.

The important part is that your frontend can rely on documented error types instead of matching message strings.

A Few Things Worth Remembering

Using Problem Details doesn't automatically solve every error-handling problem.

You still need to follow a few good practices:

  • Never expose stack traces, SQL queries, or internal secrets.
  • Keep error types stable so frontend code doesn't break.
  • Use appropriate HTTP status codes.
  • Use safe, user-friendly messages where appropriate.
  • Add a request ID when useful for debugging and support.

For localization, frontend applications can map stable error types to translated messages instead of relying on English error descriptions.

Final Thoughts

A good API isn't just about returning successful responses.

It's also about explaining failures clearly.

RFC 9457 gives us a standard way to do that.

No random error structures. No guessing from message strings. Just predictable errors that both frontend and backend developers can understand.

And the best part?

You don't need a new library or framework to get started. Just a consistent error format.

How are you currently handling API errors in your projects?

Top comments (3)

Collapse
 
challan116ux profile image
challan116-ux •

The instance field is the underrated one here — type tells the client what kind of problem it was, but instance is what lets support find that one occurrence in the logs without asking the user to reproduce it. I would add one discipline on top: the type URI has to be stable enough to code against, which means treating it like an API contract, not documentation that gets reworded. A client that switches on a human-readable message breaks on a copy edit; a client that switches on type only breaks when you mean it to.

Collapse
 
ihssmaheel profile image
Mohamed Ismail •

instance becomes really useful when it can be linked to a specific request in the logs. And I completely agree about keeping type stable. It should be treated like an API contract, not just a documentation URL. Otherwise, we're just replacing one fragile error-handling approach with another.

Collapse
 
freedailypro profile image
FreeDailyPro •

RFC 9457 is the piece I wish more APIs shipped on day one. Once every error has a stable type URI, the client stops parsing English and starts switching on a string it can depend on.

For agent callers specifically I've found detail alone isn't enough — they need a machine field like errors[].field / code they can feed back into the next attempt without guessing. A 422 with application/problem+json plus a structured invalid-params array has saved me more retries than prettier titles ever did.

Do you expose the same problem document on both REST and any RPC-style surface, or only on HTTP?