Skip to content

Error codes ​

Every error class of relaybus has a readonly code on its instances, next to its name. The code is stable: once released it is never renamed or reused, so alerts, dashboards and retry rules can branch on it. Messages may change between versions; codes do not.

ts
import { RelayBusError } from '@relaybus/core';

try {
  await bus.publish(new OrderPlaced({ orderId }));
} catch (error) {
  if (error instanceof RelayBusError && error.code === 'RELAYBUS_TRANSACTION_REQUIRED') {
    // publish inside a transaction
  }
}

Where codes appear ​

  • Dead letters and retries. The lastError of a delivery (bus.listDeliveries()) has a code when the failure was a relaybus error, for example a transport that could not publish.
  • Observer events. The error of onDeliveryRetryScheduled, onDeliveryDead and onFailure carries the same code.
  • Broker dead letters. Kafka adds relaybus-error-name and relaybus-error-code to the records it copies to <topic>.dead; RabbitMQ adds x-relaybus-dead-letter-error-name and x-relaybus-dead-letter-error-code to the messages it moves to <queue>.dead. Both keep their reason header, and their log lines carry errorName= and code=. The code header is left out for errors that have no code.
  • toErrorInfo(error) adds code for relaybus errors only. Errors of your handlers, drivers and brokers keep name, message and stack without a code, exactly as before.

The field is additive: records written before 0.3.0 have no code, so read it as optional.

Code scheme ​

RELAYBUS_ + the package for errors outside core (KAFKA_, POSTGRES_, RABBITMQ_) + the error in upper snake case. An error class you derive from RelayBusError yourself inherits RELAYBUS_ERROR unless it sets its own code.

Codes ​

CodeError classPackageMeaning
RELAYBUS_ERRORRelayBusError@relaybus/coreBase class; any relaybus error without a more specific code.
RELAYBUS_CONFIGURATIONConfigurationError@relaybus/coreInvalid options passed to a relaybus component.
RELAYBUS_CATALOG_UNKNOWN_EVENTUnknownCatalogEventError@relaybus/coreA catalog or catalog route gives a wire name to an event the catalog (or the route's selection) does not have. Extends ConfigurationError.
RELAYBUS_ROUTE_DUPLICATE_EVENTDuplicateRouteEventError@relaybus/coreA catalog route sends an event to a destination another route already sends it to. Extends ConfigurationError.
RELAYBUS_WIRE_TYPE_COLLISIONWireTypeCollisionError@relaybus/coreOne event type has two wire names on a transport, or two event types share one (catalogs, routes, codec wireTypes). Extends ConfigurationError.
RELAYBUS_WIRE_TYPES_UNSUPPORTEDWireTypesUnsupportedError@relaybus/coreWire names were declared for a transport or codec that cannot map type names (use jsonEnvelope). Extends ConfigurationError.
RELAYBUS_TRANSACTION_REQUIREDTransactionRequiredError@relaybus/corepublish ran outside a transaction with transactionMode: 'required'.
RELAYBUS_TRANSACTION_TIMEOUTTransactionTimeoutError@relaybus/coreA bus.transaction or @RelayBusTransaction() method ran past transactionTimeout and was rolled back; usually a slow external call inside it.
RELAYBUS_TRANSACTION_ISOLATION_CONFLICTTransactionIsolationConflictError@relaybus/coreA @RelayBusTransaction({ isolationLevel }) method ran inside a transaction that relaybus did not open at that level; the level of a running transaction cannot change.
RELAYBUS_NON_RETRYABLENonRetryableError@relaybus/coreA handler asked to dead-letter the delivery at once.
RELAYBUS_EVENT_VALIDATIONEventValidationError@relaybus/coreA payload failed its event schema. Never retried.
RELAYBUS_INVALID_ENVELOPEInvalidEnvelopeError@relaybus/coreA message received from a broker is not a valid event. Never retried.
RELAYBUS_LEASE_LOSTLeaseLostError@relaybus/coreThe worker no longer owns the delivery it was processing.
RELAYBUS_HANDLER_TIMEOUTHandlerTimeoutError@relaybus/coreA handler did not settle within its timeout. Retried.
RELAYBUS_DISPATCHER_STOPPEDDispatcherStoppedError@relaybus/coreThe bus stopped before the handler finished; the delivery is released.
RELAYBUS_ATTEMPTS_EXHAUSTEDAttemptsExhaustedError@relaybus/coreA delivery came back after its lease expired on its last attempt; dead-lettered without running.
RELAYBUS_LIFECYCLELifecycleError@relaybus/coreA lifecycle method was called in a state that does not allow it, such as starting a bus twice.
RELAYBUS_DRAIN_INCOMPLETEDrainIncompleteError@relaybus/coredrain still found due deliveries after its maximum number of batches.
RELAYBUS_KAFKA_INVALID_MESSAGEInvalidKafkaMessageError@relaybus/kafkaA Kafka record is not a valid event. Never retried.
RELAYBUS_KAFKA_UNENCODABLE_EVENTUnencodableEventError@relaybus/kafkaAn event cannot be written to Kafka. Never retried.
RELAYBUS_KAFKA_PUBLISH_TIMEOUTPublishTimeoutError@relaybus/kafkaNo broker ack within publishTimeout. The outbox retries.
RELAYBUS_KAFKA_HANDLER_FAILEDHandlerFailedError@relaybus/kafkaA handler failure handed back to the Kafka client.
RELAYBUS_KAFKA_TRANSPORT_CLOSEDTransportClosedError@relaybus/kafkaThe transport was closed; it no longer publishes or subscribes.
RELAYBUS_KAFKA_GROUP_JOIN_TIMEOUTGroupJoinTimeoutError@relaybus/kafkaThe consumer did not join its group within startupTimeout.
RELAYBUS_POSTGRES_RECONCILE_FAILEDReconcileFailedError@relaybus/postgresA claim was refused while giving back an earlier failed claim still fails; the next claim retries.
RELAYBUS_RABBITMQ_UNAVAILABLERabbitMqUnavailableError@relaybus/rabbitmqThe broker cannot take or confirm a message right now. Retried.
RELAYBUS_RABBITMQ_PUBLISH_REJECTEDRabbitMqPublishRejectedError@relaybus/rabbitmqThe broker nacked a published message. Retried.
RELAYBUS_RABBITMQ_UNROUTABLE_MESSAGEUnroutableMessageError@relaybus/rabbitmqNo queue is bound to the routing key. Retried.
RELAYBUS_RABBITMQ_MALFORMED_MESSAGEMalformedMessageError@relaybus/rabbitmqA received message is not a valid relaybus CloudEvent. Dead-lettered.
RELAYBUS_RABBITMQ_UNSERIALIZABLE_PAYLOADUnserializablePayloadError@relaybus/rabbitmqA payload cannot be represented as JSON. Never retried.
RELAYBUS_TESTING_CODEC_CONTRACTCodecContractError@relaybus/testingA codec contract case does not match: mismatches lists one line per difference. Thrown in tests only.
RELAYBUS_RABBITMQ_UNENCODABLE_EVENTRabbitMqUnencodableEventError@relaybus/rabbitmqThe codec cannot write the event (a reserved header name, a type the route does not carry). Never retried.
RELAYBUS_RABBITMQ_AMQP_LIMIT_EXCEEDEDAmqpLimitExceededError@relaybus/rabbitmqA value exceeds an AMQP 0-9-1 protocol limit. Never retried.

A test keeps this table in step with the code: adding, renaming or removing a code fails it until the change is deliberate.

Released under the MIT License.