@Override
  public CompletableFuture<Void> onError(Throwable throwable, final FilterRequestContext requestContext,
      final FilterResponseContext responseContext)
  {
    CompletableFuture<Void> future = new CompletableFuture<>();

    if (throwable instanceof RestLiServiceException)
    {
      RestLiServiceException restLiServiceException = (RestLiServiceException) throwable;

      // do the validation only if the 'code' field is set on RestLiServiceException.
      if (restLiServiceException.hasCode())
      {
        List<ServiceError> methodServiceErrors = requestContext.getMethodServiceErrors();
        List<ServiceError> resourceServiceErrors = requestContext.getFilterResourceModel().getServiceErrors();

        // If service error code is not defined by @ServiceErrors annotations neither on the resource level
        // nor on the method level skip the validation.
        if (methodServiceErrors == null && resourceServiceErrors == null)
        {
          // If service error is not defined neither on the resource level nor on the method level,
          // error details should not be set on RestLiServiceException object.
          if (restLiServiceException.getErrorDetailsRecord() != null)
          {
            return completeExceptionallyWithHttp500(future, restLiServiceException);
          }

          future.completeExceptionally(restLiServiceException);
          return future;
        }

        Set<ServiceError> serviceErrors = new HashSet<>();
        if (methodServiceErrors != null)
        {
          serviceErrors.addAll(methodServiceErrors);
        }

        if (resourceServiceErrors != null)
        {
          serviceErrors.addAll(resourceServiceErrors);
        }

        // An empty list of codes means that any service error code will result in a Http 500 error response.
        if (serviceErrors.isEmpty())
        {
          return completeExceptionallyWithHttp500(future, restLiServiceException);
        }

        String errorCode = restLiServiceException.getCode();

        Optional<ServiceError> maybeServiceError = serviceErrors.stream()
            .filter(serviceError -> serviceError.code().equals(errorCode)).findFirst();

        // If service error code is not defined in ServiceErrors annotation,
        // convert given throwable to 500_INTERNAL_SERVER_ERROR exception.
        if (!maybeServiceError.isPresent())
        {
          return completeExceptionallyWithHttp500(future, restLiServiceException);
        }

        ServiceError definedServiceError = maybeServiceError.get();

        // Check that the error detail type is valid.
        if (restLiServiceException.hasErrorDetails())
        {
          Class<?> errorResponseErrorDetailType = restLiServiceException.getErrorDetailsRecord().getClass();
          Class<?> definedErrorDetailType = definedServiceError.errorDetailType();

          if (!errorResponseErrorDetailType.equals(definedErrorDetailType))
          {
            return completeExceptionallyWithHttp500(future, restLiServiceException);
          }
        }

        // If http status code is not defined for the resource,
        // convert given throwable to 500_INTERNAL_SERVER_ERROR exception.
        if (definedServiceError.httpStatus() != restLiServiceException.getStatus())
        {
          return completeExceptionallyWithHttp500(future, restLiServiceException);
        }

        // TODO: validate error message. What if the defined message in service error has placeholders, which gets filled based on some business logic in the code.
      }
    }

    future.completeExceptionally(throwable);
    return future;
  }
@Test(dataProvider = "errorResponseValidationDataProvider")
  public void testErrorResponseValidation(List<ServiceError> resourceServiceErrors,
      List<ServiceError> methodServiceErrors, HttpStatus httpStatus, String serviceErrorCode,
      RecordTemplate errorDetails, HttpStatus expectedHttpStatus, String expectedErrorCode,
      RecordTemplate expectedErrorDetails)
  {
    try
    {
      when(filterRequestContext.getFilterResourceModel()).thenReturn(resourceModel);
      when(resourceModel.getServiceErrors()).thenReturn(resourceServiceErrors);
      when(filterRequestContext.getMethodServiceErrors()).thenReturn(methodServiceErrors);

      ErrorResponseValidationFilter validationFilter = new ErrorResponseValidationFilter();

      RestLiServiceException restLiServiceException = new RestLiServiceException(httpStatus);
      restLiServiceException.setCode(serviceErrorCode);
      restLiServiceException.setErrorDetails(errorDetails);

      CompletableFuture<Void> future = validationFilter.onError(restLiServiceException,
          filterRequestContext, filterResponseContext);
      Assert.assertTrue(future.isCompletedExceptionally());

      future.get();
    }
    catch (Exception exception)
    {
      if (exception.getCause() != null && exception.getCause() instanceof RestLiServiceException)
      {
        RestLiServiceException restLiServiceException = (RestLiServiceException) exception.getCause();

        Assert.assertEquals(restLiServiceException.getStatus(), expectedHttpStatus);
        Assert.assertEquals(restLiServiceException.getCode(), expectedErrorCode);
        Assert.assertEquals(restLiServiceException.getErrorDetailsRecord(), expectedErrorDetails);
      }
      else
      {
        Assert.fail("Expected to get only RestLiServiceException.");
      }
    }
  }