Handle Errors with Composite Resources


In a previous blog post titled “Simplify Your API Code with New Composite Resources” we talked about the new composite resources in the Salesforce REST API. The composite resources provide a way to “batch” together several REST API calls in a single call, improving the performance of your app by reducing the number of round-trips to Salesforce, and simplifying your code.

However, now that you’re making several calls in a single composite resource call, what happens when one of those calls runs into a problem? How will your app gracefully handle the scenario where some operations in your composite call succeed and some operations fail? Fortunately, the composite resources have you covered.

Note that all composite resources return detailed information when any error occurs. It’s always a good idea to check the full response and make sure everything went according to plan.

High Level Parsing Errors

Since composite resources can have fairly complex request formats, Salesforce does some sanity-checks on the overall request body content before processing subrequests. If any error in the request body format is detected, Salesforce returns a 400 “Bad Request” error without processing any of the subrequests. An example where this could occur is in a composite resource call, if a subrequest is missing the required “referenceId” field this is treated as request error. The error response might look something like this:

HTTP/1.1 400 Bad Request
...
[ {
  "message" : "'referenceId' is a required field.",
  "errorCode" : "JSON_PARSER_ERROR"
} ]

If the request body is valid, Salesforce starts processing subrequests. Subrequests themselves could fail for a variety of reasons during processing, and each composite resource provides appropriate subrequest error information in the response data.

Errors in batch Resource Calls

If any subrequests result in errors in a batch resource call, the response will have a hasErrors field with a value of “true”. This field simply indicates that there were one or more errors in the list of subrequest calls. You’ll still need to examine the results array to determine which subrequests failed. Suppose you used the following request body in a batch resource call:

{
"batchRequests" : [
    {
    "method" : "GET",
    "url" : "v38.0/sobjects/Account/describe/"
    },{
    "method" : "GET",
    "url" : "v38.0/notARealResource/"
    }]
}

Notice the cleverly named “notARealResource” resource call, which we expect to fail. The response array would contain the results of the successful Account describe call, and the invalid “notARealResource” call:

HTTP/1.1 200 OK
...
{
  "hasErrors" : true,
  "results" : [ {
    "statusCode" : 200,
    "result":
    {
    ... result of Account describe ...
    }, {
    "result" : [ {
      "errorCode" : "NOT_FOUND",
      "message" : "The requested resource does not exist"
    } ],
    "statusCode" : 404
  } ]
} 

By default, the batch resource processes all the subrequests in a batch, even if some of the subrequests fail. However, you can specify that Salesforce stop processing subrequests once a subrequest fails by using the haltOnError request field. If haltOnError is true, and a subrequest has an error, Salesforce won’t attempt to execute any subrequests that come after the subrequest that failed.

Here’s a simple example to help clarify this. Suppose we made a batch request with the following request body:

{
"haltOnError" : "true",
"batchRequests" : [
    {
    "method" : "GET",
    "url" : "v38.0/sobjects/Account/describe/"
    },{
    "method" : "GET",
    "url" : "v38.0/notARealResource/"
    },{
    "method" : "GET",
    "url" : "v38.0/sobjects/Contact/describe/"
    }]
}

When we make a batch call with this request body, we get a response that has:

  • hasErrors set to true, since there was one error.
  • An array of subrequest results which will be the results of a successful Account describe call, followed by a 404 “Not found” error result for the bad “notARealResource” call, followed by a 412 “Batch processing halted” error result indicating Salesforce stopped processing the batch and didn’t process the Contact describe call.

If we had set haltOnError to false, the results would be similar, but the response would contain the results of the successful Contact describe call instead of a 412 “Batch processing halted”.

Errors in tree Resource Calls

For the tree resource, if there’s any error when creating a record, none of the records in the request are created. The response will contain the hasErrors field set to true, and an array of records that failed, along with the reference ID you supplied for the record and detailed error information for the failure.

For example, suppose you made a tree resource call that included a SObject tree that looked like this:

{
"records" :[
  ...
  {
    "attributes" : {"type" : "Account", "referenceId" : "ref1"},
    "name" : "BadAccount1",
    "phone" : "hello this is a really long string that should not work...",
  },{
  ...
  }]
}

The phone field is invalid, and causes an error when attempting to create the record. The response you get clarifies the source of the error:

{
  "hasErrors" : true,
  "results" : [ {
    "referenceId" : "ref1",
    "errors" : [ {
      "statusCode" : "STRING_TOO_LONG",
      "message" : "Account Phone: data value too large: hello this is a really long string that should not work... (max length=40)",
      "fields" : [ "Phone" ]
    } ]
  } ]
}

Errors in composite Resource Calls

If a subrequest fails during processing with a composite resource call, the error is returned in the response in the results array. Suppose you used a request body that looked something like this:

{
  "compositeRequest" : [
  {
    "method" : "POST",
    "url" : "/services/data/v38.0/sobjects/Account",
    "referenceId" : "okAccount",
    "body" : { 
      "Name" : "Ok Account" 
    }
  },{
    "method" : "GET",
    "url" : "v38.0/notARealResource/",
    "referenceId" : "badMethod"
  }] 
}

In the response, you’d get an array of two items. The first item would have the results of creating a new Account record. The second item would contain a 400 error indicating “notARealResource” is, in fact, not a real resource:

HTTP/1.1 200 OK
...
{
  "compositeResponse" : [ {
    "body" : {
      "id" : "001R0000003GzOCIA0",
      "success" : true,
      "errors" : [ ]
    },
    "httpHeaders" : {
      "Location" : "/services/data/v38.0/sobjects/Account/001R0000003GzOCIA0"
    },
    "httpStatusCode" : 201,
    "referenceId" : "okAccount"
  }, {
    "body" : [ {
      "errorCode" : "PROCESSING_HALTED",
      "message" : "'v38.0/notARealResource/' is not a valid url"
    } ],
    "httpHeaders" : { },
    "httpStatusCode" : 400,
    "referenceId" : "badMethod"
  } ]
}

Similar to the batch resource, the composite resource provides a way to control what happens if a subrequest fails. By default other subrequests are still processed, but by setting allOrNone to true in the request body, you can tell Salesforce to stop processing any subrequests once an error occurs and rollback any subrequests that completed before the error happened.

More Information

Hopefully you’ve now got a feel for how the composite resources handle errors, and how you can control what happens when a subrequest fails. Here’s a quick recap of the composite resources, suggested use cases, and subrequest error handling, in convenient table form:

Resource Use Case Has Subrequest Error Flow Control?
batch Combine unrelated subrequests into a single request. Yes, using haltOnError
tree Create a set of records, with parent-child relationships if needed, in a single request. No. If the request fails, no records are created.
composite Combine subrequests into a single request. Subrequests can have dependencies on other subrequests. Yes, using allOrNone

 

Feel free to check out the official docs, which include a series of more complex examples. For more information on the composite resources, see:

Examples and walkthroughs of using the composite resources in the REST API Developer Guide.
Reference documentation on the composite resources in the REST API Developer Guide.

tagged , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • Daniel Ballinger

    The polymorphic nature of the Batch subrequest result make parsing it in something like Apex problematic.
    The inner result type varies based on the request and if any errors occured.

    Try making an Apex JSON parser for something like the following where one record is created and one has an invalid id. One moment the inner result is a single object, the next it is a list of errors.


    {
    "hasErrors": true,
    "results": [
    {
    "statusCode": 201,
    "result": {
    "id": "00kn00000000001AAA,
    "success": true,
    "errors": []
    }

    },
    {
    "result": [
    {
    "errorCode": "INVALID_CROSS_REFERENCE_KEY",
    "message": "invalid cross reference id"
    }
    ],
    "statusCode": 400
    }
    ]
    }

    Something like http://json2apex.herokuapp.com/ can’t cleanly create a parser for it as it isn’t clear what type is being returned.