SaFi Bank Space : SaFi Proposal: Global Error Handling

The proposed approach is aimed to fulfill the goals of having a common way of handling errors across all sub-projects, and having a common error response JSON format. Judging from past experience, the following concerns require special attention:

  1. Using error codes for localisation of error messages. As a more complex case, using an error code to match a localised message template and provide arguments as a separate list in the response.

  2. Covering the case of multiple errors, which typically takes places when using a Validation API or performing batch operations.

At present, our error responses contain only the message field, which is not enough in the long term perspective.

Partly, global error handling is implemented in loan-manager so that all exceptions are treated in the same way, and all error responses have the same JSON structure. However, the latter is subject to further discussion. In the scope of this proposal we suggest to adopt one of such formats:

The default Micronaut error response
{
  "message": "Not Found",
  "logref": null,
  "path": null,
  "_embedded": {
    "errors": [
      {
        "message": "Overdraft not found",
        "logref": null,
        "path": "/loan/overdraft/96b1b69c-a3b6-4218-08ec-28c697c7b79b/repay",
        "_embedded": {},
        "_links": {}
      }
    ]
  },
  "_links": {
    "self": {
      "href": "/loan/overdraft/96b1b69c-a3b6-4218-08ec-28c697c7b79b/repay",
      "templated": false,
      "profile": null,
      "deprecation": null,
      "title": null,
      "hreflang": null,
      "type": null,
      "name": null
    }
  }
}

Pros: it’s already there, no need to invent anything new

Cons: there are a lot of HATEOAS fields which we do not use

Custom error response
{
    "message": "Validation failed",
    "status": 400,
    "code": "VALIDATION_FAILED",
    "path": "/api/v1/my-endpoint?param1=val1",
    "timestamp": "2022-07-14T12:00:00Z",
    "errors": [
        {
            "code": "VALIDATION_FAILED",
            "message": "Validation failed for parameter param1",
            "parameter": "param1"
        },
        {
            "code": "VALIDATION_FAILED",
            "message": "Validation failed for object='exampleRequestBody'. Error count: 2",
            "fieldErrors": [
                {
                    "code": "INVALID_SIZE",
                    "field": "name",
                    "message": "size must be between 10 and 2147483647",
                    "rejectedValue": ""
                },
                {
                    "code": "REQUIRED_NOT_BLANK",
                    "field": "favoriteMovie",
                    "message": "must not be blank",
                    "rejectedValue": null
                }
            ]
        },
        {
            "code": "GENERIC_ERROR",
            "message": "Something went wrong",
            "stacktrace": "Filtered stacktrace enabled by a flag only for debugging purposes"
        }
    ]
}

Pros: we can shape it anyway we like

Cons: requires more work on design and implementation; it is not easy to find a good example of an existing API to adopt

Alternative

Other options would be to use Problem Details for HTTP APIs RFC 7807 (probably, not so widely used), or mimic a well known API. E.g. Twitter’s approach looks fine.

https://developer.twitter.com/en/docs/twitter-ads-api/response-codes

https://nordicapis.com/best-practices-api-error-handling/

https://cloud.google.com/storage/docs/json_api/v1/status-codes