Errors

Errors in CAF have a code and a category, similar to std::error_code and std::error_condition. Unlike its counterparts from the C++ standard library, error is plattform-neutral and serializable.

Class Interface

Constructors  
(Enum code) Constructs an error with given error code.
(Enum code, messag context) Constructs an error with given error code and additional context.
   
Observers  
uint8_t code() Returns the error code as 8-bit integer.
type_id_t category() Returns the type ID of the Enum type used to construct this error.
message context() Returns additional context information
explicit operator bool() Returns code() != 0

Add Custom Error Categories

Adding custom error categories requires these steps:

  • Declare an enum class of type uint8_t with error codes starting at 1. CAF always interprets the value 0 as no error.
  • Assign a type ID to your enum type.
  • Specialize caf::is_error_code_enum for your enum type. For this step, CAF offers the macro CAF_ERROR_CODE_ENUM to generate the boilerplate code necessary.

The following example illustrates all these steps for a custom error code enum called math_error.

enum class math_error : uint8_t {
  division_by_zero = 1,
};

std::string to_string(math_error x) {
  switch (x) {
    case math_error::division_by_zero:
      return "division_by_zero";
    default:
      return "-unknown-error-";
  }
}

bool from_string(caf::string_view in, math_error& out) {
  if (in == "division_by_zero") {
    out = math_error::division_by_zero;
    return true;
  } else {
    return false;
  }
}

bool from_integer(uint8_t in, math_error& out) {
  if (in == 1) {
    out = math_error::division_by_zero;
    return true;
  } else {
    return false;
  }
}

template <class Inspector>
bool inspect(Inspector& f, math_error& x) {
  return caf::default_enum_inspect(f, x);
}

CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)

  CAF_ADD_TYPE_ID(divider, (math_error))

CAF_END_TYPE_ID_BLOCK(divider)

CAF_ERROR_CODE_ENUM(math_error)

Default Error Codes

The enum type sec (for System Error Code) provides many error codes for common failures in actor systems:

/// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules.
enum class sec : uint8_t {
  /// No error.
  none = 0,
  /// Indicates that an actor dropped an unexpected message.
  unexpected_message = 1,
  /// Indicates that a response message did not match the provided handler.
  unexpected_response,
  /// Indicates that the receiver of a request is no longer alive.
  request_receiver_down,
  /// Indicates that a request message timed out.
  request_timeout,
  /// Indicates that requested group module does not exist.
  no_such_group_module = 5,
  /// Unpublishing or connecting failed: no actor bound to given port.
  no_actor_published_at_port,
  /// Connecting failed because a remote actor had an unexpected interface.
  unexpected_actor_messaging_interface,
  /// Migration failed because the state of an actor is not serializable.
  state_not_serializable,
  /// An actor received an unsupported key for `('sys', 'get', key)` messages.
  unsupported_sys_key,
  /// An actor received an unsupported system message.
  unsupported_sys_message = 10,
  /// A remote node disconnected during CAF handshake.
  disconnect_during_handshake,
  /// Tried to forward a message via BASP to an invalid actor handle.
  cannot_forward_to_invalid_actor,
  /// Tried to forward a message via BASP to an unknown node ID.
  no_route_to_receiving_node,
  /// Middleman could not assign a connection handle to a broker.
  failed_to_assign_scribe_from_handle,
  /// Middleman could not assign an acceptor handle to a broker.
  failed_to_assign_doorman_from_handle = 15,
  /// User requested to close port 0 or to close a port not managed by CAF.
  cannot_close_invalid_port,
  /// Middleman could not connect to a remote node.
  cannot_connect_to_node,
  /// Middleman could not open requested port.
  cannot_open_port,
  /// A C system call in the middleman failed.
  network_syscall_failed,
  /// A function received one or more invalid arguments.
  invalid_argument = 20,
  /// A network socket reported an invalid network protocol family.
  invalid_protocol_family,
  /// Middleman could not publish an actor because it was invalid.
  cannot_publish_invalid_actor,
  /// A remote spawn failed because the provided types did not match.
  cannot_spawn_actor_from_arguments,
  /// Serialization failed because there was not enough data to read.
  end_of_stream,
  /// Serialization failed because no CAF context is available.
  no_context = 25,
  /// Serialization failed because CAF misses run-time type information.
  unknown_type,
  /// Serialization of actors failed because no proxy registry is available.
  no_proxy_registry,
  /// An exception was thrown during message handling.
  runtime_error,
  /// Linking to a remote actor failed because actor no longer exists.
  remote_linking_failed,
  /// Adding an upstream to a stream failed.
  cannot_add_upstream = 30,
  /// Adding an upstream to a stream failed because it already exists.
  upstream_already_exists,
  /// Unable to process upstream messages because upstream is invalid.
  invalid_upstream,
  /// Adding a downstream to a stream failed.
  cannot_add_downstream,
  /// Adding a downstream to a stream failed because it already exists.
  downstream_already_exists,
  /// Unable to process downstream messages because downstream is invalid.
  invalid_downstream = 35,
  /// Cannot start streaming without next stage.
  no_downstream_stages_defined,
  /// Actor failed to initialize state after receiving a stream handshake.
  stream_init_failed,
  /// Unable to process a stream since due to missing state.
  invalid_stream_state,
  /// Stream aborted due to unexpected error.
  unhandled_stream_error,
  /// A function view was called without assigning an actor first.
  bad_function_call = 40,
  /// Feature is disabled in the actor system config.
  feature_disabled,
  /// Failed to open file.
  cannot_open_file,
  /// A socket descriptor argument is invalid.
  socket_invalid,
  /// A socket became disconnected from the remote host (hang up).
  socket_disconnected,
  /// An operation on a socket (e.g. `poll`) failed.
  socket_operation_failed = 45,
  /// A resource is temporarily unavailable or would block.
  unavailable_or_would_block,
  /// Connection refused because of incompatible CAF versions.
  incompatible_versions,
  /// Connection refused because of incompatible application IDs.
  incompatible_application_ids,
  /// The middleman received a malformed BASP message from another node.
  malformed_basp_message,
  /// The middleman closed a connection because it failed to serialize or
  /// deserialize a payload.
  serializing_basp_payload_failed = 50,
  /// The middleman closed a connection to itself or an already connected node.
  redundant_connection,
  /// Resolving a path on a remote node failed.
  remote_lookup_failed,
  /// Serialization failed because actor_system::tracing_context is null.
  no_tracing_context,
  /// No request produced a valid result.
  all_requests_failed,
  /// Deserialization failed because an invariant got violated after reading
  /// the content of a field.
  field_invariant_check_failed = 55,
  /// Deserialization failed because a setter rejected the input.
  field_value_synchronization_failed,
  /// Deserialization failed because the source announced an invalid type.
  invalid_field_type,
  /// Serialization failed because a type was flagged as unsafe message type.
  unsafe_type,
  /// Serialization failed because a save callback returned `false`.
  save_callback_failed,
  /// Deserialization failed because a load callback returned `false`.
  load_callback_failed = 60,
  /// Converting between two types failed.
  conversion_failed,
  /// A network connection was closed by the remote side.
  connection_closed,
  /// An operation failed because run-time type information diverged from the
  /// expected type.
  type_clash,
  /// An operation failed because the callee does not implement this
  /// functionality.
  unsupported_operation,
  /// A key lookup failed.
  no_such_key = 65,
  /// An destroyed a response promise without calling deliver or delegate on it.
  broken_promise,
  /// Disconnected from a BASP node after reaching the connection timeout.
  connection_timeout,
};

Default Exit Reasons

A special kind of error codes are exit reasons of actors. These errors are usually fail states set by the actor system itself. The two exceptions are exit_reason::user_shutdown and exit_reason::kill. The former is used in CAF to signalize orderly, user-requested shutdown and can be used by programmers in the same way. The latter terminates an actor unconditionally when used in send_exit, even for actors that override the default handler (see Exit Handler).

/// This error category represents fail conditions for actors.
enum class exit_reason : uint8_t {
  /// Indicates that an actor finished execution without error.
  normal = 0,
  /// Indicates that an actor died because of an unhandled exception.
  unhandled_exception [[deprecated("superseded by sec::runtime_error")]],
  /// Indicates that the exit reason for this actor is unknown, i.e.,
  /// the actor has been terminated and no longer exists.
  unknown,
  /// Indicates that an actor pool unexpectedly ran out of workers.
  out_of_workers,
  /// Indicates that an actor was forced to shutdown by a user-generated event.
  user_shutdown,
  /// Indicates that an actor was killed unconditionally.
  kill,
  /// Indicates that an actor finishied execution because a connection
  /// to a remote link was closed unexpectedly.
  remote_link_unreachable,
  /// Indicates that an actor was killed because it became unreachable.
  unreachable
};