Skip to main content

Error Handling

Bolt ships with Miraganic Error Handling, a custom plugin for handling errors in blueprint and C++. This plugin has a few goals:

  • Make it possible to pass errors between C++ and Blueprint.
  • Make defining error types easier in both C++ and Blueprint.
  • Pass errors across interfaces. This isn't possible with a static enum type.
  • Give feedback to players depending on the error that occurs. (E.g., 'Can't deposit items because the stack is full')
  • Allow functions to decide whether they want to handle errors or simply log them.
  • Return errors using a lightweight type. Handling errors this way shouldn't affect performance.

Enum Error Handling

Let's use a simple example: dividing. Dividing by zero causes an error. Without the error handling plugin, we might return this error using an enum, like so:

Error Enum Safe Divide

ECResult

This is simple and works well for this small function, but what if we want to define an interface using this error handling method? We don't know all of the possible errors for our implementors, so it's not possible to define them in an enumeration. This is where the ECResult type comes in. ECResult is essentially an 'any enum'; it stores the type of enumeration and the value. In the case above, it would store EDivisionResult and 0 or 1 depending on the value. Let's look at a function that's meant to be overridden from the plugin:

In blueprint, you can create new errors using `ErrorCategory`s. These are defined just like enums, but the value '0' is always reserved for success.

Error Category A Item Stack Combine

Returning Results

Both of these implementors can define their own errors and return them. Not only this, defining an error is the same as our previous method using an enum. This is great, but how can we handle the error(s) returned from the function?

In Blueprints, you may use the Switch on Result node. By default, this node always has two pins: Success and Default. You may add pins for specific errors that you want to handle. The pins are processed from the top to the bottom; Default will catch any errors that you haven't explicitly added.

Result Pass To Caller

Logging

Most bolt functions do not log results on their own. Rather, they let callers decide whether the results should be logged:

Log If Failure

Errors In Bolt

Bolt functions are typically split into three parts: Can, Exec, and Try. The Can part checks whether it's possible to perform the action (e.g., whether depositing an item stack is allowed at all). Can functions never make any state changes. Exec is responsible for actually performing the changes. Try functions call Can, then Exec:

  • Can I do this?
    • Yes -> Exec
    • No -> return the error that occurred.

If a Try function fails, it won't change any state.