Why was the decision made that data and errors can't coexist?

I’m interested in finding a good and thorough standard for JSON-based APIs to help my team stay on the same page with all of our API responses and facilitate communication. To that end, JSON API seems to be the most popular, or at least the most widely linked, on the internet. But off the bat at the very top of the specification I noticed something that just seems weird to me.

The members data and errors MUST NOT coexist in the same document.

I’m interested in understanding the reason for this decision. At the surface is seems reasonable but I can think of two really good reasons that this requirement doesn’t make sense:

  1. Statically typed programming languages don’t have a way to represent two members which are mutually exclusive. This means any C++, Java, C#, TypeScript, or similar SDK trying to ingest a JSON API based endpoint will run into issues. The Document object must contain both a data member and an errors member, but attempting to serialize such an object will produce an invalid document.
  2. Because a REST API could sit in front of ANY source of data, and not just a database, it’s possible that valid data may be returned and errors may exist – particularly with collections. Consider an endpoint which pings several servers and returns their response times. If one of the servers is offline then you have an error, but the existence of that error doesn’t invalidate the data from the other servers.

I imagine the intention was to make it trivial to test for the existence of an error using something like:

fetch("/api/endpoint").then(resp => resp.json()).then(resp => {
    if (resp.errors) {
        // Handle errors
    } else {
        // Utilize data
    }
});

However this could just as easily be accomplished with:

    if (resp.errors.length > 0) {

So could someone explain the reason that “MUST NOT” was included in the specification here? Would it be unreasonable for version 1.1 to replace this with just “it is RECOMMENDED that these two members not coexist”, or similar?

To your point #1: I have a JSON:API library written in Go. That as never been a problem. The Document struct has both a Data field and an Errors field. If len(Errors) > 0, I know I have to marshal some errors, otherwise I marshal the data.

The shape of the document and your data structure are different things. They don’t have to be the same. If you need them to be the same because that what’s the library you’re using for JSON marshaling expects, then that’s your limitation.

To your point #2: I think you are confusing different kinds of errors. In your example, nothing went wrong. You pinged some servers as expected, and for each ping you got a successful result. Here’s the thing, if a server is offline, then you still successfully managed to get the result, which is offline instead of something like 42ms.

Your payload then successfully returns a bunch of results in a collection, like 42ms, 8ms, 71ms, and offline.

Errors are for the request between the client and the server. An error occurs when the client or the server can’t properly do their job. For example, the client wants the server to ping an/invalid.domain or the server had an unexpected error.

1 Like

I was going to start a new topic but this is pretty much the basis of my problem.

I have an API that returns a data response with most of the attributes populated with values. Some attributes are optional and the server may have had a problem generating a value for these attributes.

I feel its right that I inform the client that the logic behind these attributes was not successful at obtaining the data, even though the client can still function without it.

I had intended to use the errors collection and the errors structure with the pointer attribute to show what attribute(s) we had a problem with along with the title and detail.

This would have been really convenient had it not been for the statement in the spec saying, "data and errors MUST NOT coexist in the same document"

If I cannot use both errors and data in the same response, how should I go about supporting this requirement in json api?

@fletc: It sounds like these aren’t really considered errors, so much as sometimes these particular fields aren’t available. I’d be inclined not to generate errors, but to record in the meta for the resource that the fields could not be computed. Additional information about why could be included there are well:

{"data": {
    "id": "42",
    "type": "my-thing",
    "attributes": {...},
    "meta": {
        "missing_attributes": {
            "attrname": {
                "why": "error retrieving data",
                "retry_later": true
            },
            "another": {
                "why": "bad response from service",
                "code": 500,
                "retry_later": false
            }
        }
    }
 }

@fdrake : Thanks I guess that makes sense, though the structure for errors seems to be ideal for supporting this information, just seems a bit of a shame not to make use of it.

I guess we can build a similar structure in meta to support the requirement.

It seems to be a common requirement for projects I work on these days. The systems need to be pretty fault tolerant, so I was a bit surprised it was not supported in a more structured way in the specification. Your right in that it doesn’t result in an HTTP response error for the resource, but the client may sometimes need to take further action or simply log the fact that something didn’t quite go as expected.