Error handling rules#

Error handling in a SYCL application (host code) uses C++ exceptions. If an error occurs, it will be thrown by the API function call and may be caught by the user through standard C++ exception handling mechanisms.

SYCL applications are asynchronous in the sense that host and device code executions are decoupled from one another except at specific points.

For example, device code executions often begin when dependencies in the SYCL task graph are satisfied, which occurs asynchronously from host code execution. As a result of this the errors that occur on a device cannot be thrown directly from a host API call, because the call enqueueing a device action has typically already returned by the time that the error occurs. Such errors are not detected until the error-causing task executes or tries to execute, and we refer to these as asynchronous errors. For handling those type of errors see Asynchronous error handler.

Priorities of asynchronous handlers#

If the SYCL runtime can associate an asynchronous error with a specific queue, then:

If the SYCL runtime cannot associate an asynchronous error with a specific queue, then:

Asynchronous errors with a secondary queue#

If an asynchronous error occurs when running or enqueuing a command group which has a secondary queue specified, then the command group may be enqueued to the secondary queue instead of the primary queue. The error handling in this case is also configured using the sycl::async_handler provided for both queues.

If there is no sycl::async_handler given on any of the queues, then the asynchronous error handling proceeds through the contexts associated with the queues, and if they were also constructed without sycl::async_handlers, then the default handler will be used.

If the primary queue fails and there is an sycl::async_handler given at this queue's construction, which populates the sycl::exception_list parameter, then any errors will be added and can be thrown whenever the user chooses to handle those exceptions. Since there were errors on the primary queue and a secondary queue was given, then the execution of the kernel is re-scheduled to the secondary queue and any error reporting for the kernel execution on that queue is done through that queue, in the same way as described above. The secondary queue may fail as well, and the errors will be thrown if there is an sycl::async_handler and either wait_and_throw() or throw() are called on that queue.

If no sycl::async_handler was specified, then the one associated with the queue's context will be used and if the context was also constructed without an sycl::async_handler, then the default handler will be used. The command group function object event returned by that function will be relevant to the queue where the kernel has been enqueued.

Examples of catching synchronous errors#

Below is an example of catching a sycl::exception (see Exceptions) and printing out the error message.

void catch_any_errors(sycl::context const& ctx) {
  try {
    do_something_to_invoke_error(ctx);
  } catch (sycl::exception const& e) {
    std::cerr << e.what();
  }
}

Below is an example of catching a sycl::exception (see Exceptions) with the sycl::errc::invalid error code and printing out the error message.

void catch_invalid_errors(sycl::context const& ctx) {
  try {
    do_something_to_invoke_error(ctx);
  } catch (sycl::exception const& e) {
    if (e.code() == sycl::errc::invalid) {
      std::cerr << "Invalid error: " << e.what();
    } else {
      throw;
    }
  }
}