Bondy Connect
Skip to content

Error Reference ​

Every failure Bondy reports to a peer is one error value, projected onto the transport in use. A WAMP ERROR carries the error's URI as its ErrorUri, one human-readable sentence as Args[0], and the payload below as KWArgs. An HTTP API Gateway response carries the same payload as its JSON body, with a status code derived from the same URI. Identifying a failure means the same thing on both transports.

The error payload ​

json
{
  "uri":         "bondy.error.invalid_value",
  "code":        "invalid_value",
  "handle":      "C010",
  "nature":      "permanent",
  "message":     "The operation failed due to an invalid value.",
  "description": "The value for property 'email' is invalid.",
  "details":     { "key": "email", "value": "not-an-address" },
  "causes":      [],
  "doc_uri":     "/errors/C010"
}
FieldContract
uriThe error's identity, and the only field to branch on. Always present, always a valid URI.
codeA short token retained for compatibility with the payloads Bondy emitted before this catalogue existed. Fixed per error type, and not always derivable from uri. Do not branch on it in new code.
handleA stable identifier for support and documentation, such as C010. Never interpreted by software.
naturetransient or permanent. See Retry semantics.
messageOne sentence, safe to show a user. Also Args[0] of a WAMP ERROR, except for wamp.error.not_authorized raised by a Bondy administration procedure, whose Args[0] is this whole payload map.
descriptionA longer explanation, safe to show a user. May be empty.
detailsStructured context: the offending key, the limit reached, the realm that was missing. Values are JSON scalars, lists or maps.
causesErrors that led to this one, outermost first. Each entry has this same shape. Usually empty.
doc_uriPath to this error's entry in the catalogue.
trace_idPresent only on an internal error. A W3C Trace Context trace-id: 32 lowercase hex characters.

Branch on uri, not code

code exists so that clients written against older Bondy releases keep working. It is stable, but it is not a single vocabulary: for some errors it is a bare token and for others a full URI. uri is the identity.

Every key of details also appears at the top level of the payload, because key, value, limit and keys have always been read from there. Read details; the top-level copies exist for older clients.

What a peer never receives ​

Stacktraces, exception reasons and internal terms are held outside the payload and written only to the log. A failure Bondy cannot explain safely is reported as bondy.error.internal_error with a trace_id and nothing else. The same trace_id appears on the server log entry holding the actual reason, so an operator can join the two without the peer ever seeing it.

Every value in the payload is JSON-encodable by construction. A term with no JSON representation is rendered as text. Within details, a value nested deeper than 8 levels is rendered as text, and each string is truncated to about 4096 bytes, ending in .... Bondy does not truncate message or description, and does not bound the total size of the payload.

Correlating requests and errors ​

Bondy generates the trace_id of an internal error from a UUIDv7, so trace IDs sort by creation time. The HTTP API Gateway gives every request an ID of the same kind when the request arrives. An API specification reads that ID as {{request.id}}. When an action fails, Bondy logs the error against the same ID, and the specification can return it to the client as {{action.error.id}}. The log record carries the value that the client receives, so either ID locates the record of a failure.

Retry semantics ​

nature tells a client whether retrying can succeed.

transient means the condition is in the server, the cluster or the network. The same request may succeed later, so retry with backoff. bondy.error.temporarily_unavailable is worth singling out: the node has not yet confirmed its security state with the rest of the cluster, so another node may answer immediately.

permanent means the request itself is the problem. Retrying it unchanged fails identically. Change the request, the credentials or the permissions.

Two URIs that are easy to confuse

wamp.error.not_authorized is the router refusing a peer, and is permanent. wamp.error.authorization_failed is the router being unable to decide whether the operation is permitted, and is transient.

The catalogue ​

HTTP is the status an API Gateway response uses. An API specification can override it per host or version through its status_codes map. An error URI with no HTTP mapping answers 500 unless the specification maps it. A † marks a type that shares another's URI.

The error URIs that the administrative WAMP API raises have a page each under WAMP API Errors. Each page says which procedures raise the error and what its details carry.

Authentication ​

TypeURICodeNatureHTTPHandle
invalid_credentialsbondy.error.invalid_credentialsinvalid_credentialspermanent401A001
authentication_failedwamp.error.authentication_failedauthentication_failedpermanent401A002
token_expiredbondy.error.token_expiredtoken_expiredpermanent401A003
token_invalidbondy.error.token_invalidtoken_invalidpermanent401A004
not_auth_methodwamp.error.not_auth_methodnot_auth_methodpermanent400A005
no_such_principalwamp.error.no_such_principalno_such_principalpermanent400A006
no_such_userwamp.error.no_such_principalwamp.error.no_such_principalpermanent400A007 †

Authorization ​

TypeURICodeNatureHTTPHandle
forbiddenbondy.error.forbiddenforbiddenpermanent403Z001
not_authorizedwamp.error.not_authorizednot_authorizedpermanent403Z002
authorization_failedwamp.error.authorization_failedauthorization_failedtransient500Z003
insufficient_permissionsbondy.error.insufficient_permissionsinsufficient_permissionspermanent403Z004
role_not_allowedbondy.error.role_not_allowedrole_not_allowedpermanent403Z005
no_such_rolewamp.error.no_such_roleno_such_rolepermanent400Z006
unauthorizedwamp.error.not_authorizednot_authorizedpermanent403Z007 †

Client and request ​

TypeURICodeNatureHTTPHandle
bad_requestbondy.error.bad_requestbad_requestpermanent400C001
invalid_requestbondy.error.invalid_requestinvalid_requestpermanent400C002
not_foundbondy.error.not_foundnot_foundpermanent404C003
already_existsbondy.error.already_existsalready_existspermanent400C004
method_not_allowedbondy.error.method_not_allowedmethod_not_allowedpermanent405C005
request_timeoutbondy.error.request_timeoutrequest_timeouttransient408C006
timeoutwamp.error.timeouttimeouttransient504C007
argument_errorwamp.error.invalid_argumentinvalid_argumentpermanent400C008 †
invalid_argumentwamp.error.invalid_argumentinvalid_argumentpermanent400C009
invalid_valuebondy.error.invalid_valueinvalid_valuepermanent400C010
missing_required_valuebondy.error.missing_required_valuemissing_required_valuepermanent400C011
property_range_limitbondy.error.property_range_limitproperty_range_limitpermanent400C012
inconsistency_errorbondy.error.inconsistency_errorinvalid_argumentpermanent400C013
invalid_databondy.error.invalid_datainvalid_datapermanent400C014
invalid_uriwamp.error.invalid_uriinvalid_uripermanent400C015
conflictbondy.error.conflictconflictpermanent409C016
proxy_protocol_errorbondy.error.proxy_protocol_errorproxy_protocol_errorpermanent403C017
badargwamp.error.invalid_argumentinvalid_argumentpermanent400C018 †
not_implementedbondy.error.not_implementednot_implementedpermanent500C019

Limits ​

TypeURICodeNatureHTTPHandle
rate_limit_exceededbondy.error.rate_limit_exceededrate_limit_exceededtransient429L001
quota_exceededbondy.error.quota_exceededquota_exceededpermanent429L002
too_many_requestsbondy.error.too_many_requeststoo_many_requeststransient429L003
too_many_sessionsbondy.error.too_many_sessionstoo_many_sessionstransient429L004
too_large_payloadbondy.error.too_large_payloadtoo_large_payloadpermanent413L005
too_many_resultsbondy.error.too_many_resultstoo_many_resultspermanent400L006
body_max_bytes_exceededbondy.error.body_max_bytes_exceededbody_max_bytes_exceededpermanent400L007
payload_size_exceededwamp.error.payload_size_exceededpayload_size_exceededpermanent413L008

API Gateway and OAuth2 ​

TypeURICodeNatureHTTPHandle
oauth2_invalid_requestbondy.error.invalid_requestinvalid_requestpermanent400G001 †
oauth2_invalid_clientbondy.error.invalid_clientinvalid_clientpermanent401G002
oauth2_invalid_grantbondy.error.invalid_grantinvalid_grantpermanent400G003
oauth2_unauthorized_clientbondy.error.unauthorized_clientunauthorized_clientpermanent400G004
oauth2_unsupported_grant_typebondy.error.unsupported_grant_typeunsupported_grant_typepermanent400G005
oauth2_invalid_scopebondy.error.invalid_scopeinvalid_scopepermanent400G006
unsupported_token_typebondy.error.unsupported_token_typeunsupported_token_typetransient503G007
invalid_schemebondy.error.invalid_clientinvalid_clientpermanent401G008 †
invalid_expressionbondy.error.http_gateway.invalid_expressionbondy.error.http_gateway.invalid_expressionpermanent500G009

WAMP protocol ​

TypeURICodeNatureHTTPHandle
no_such_realmwamp.error.no_such_realmwamp.error.no_such_realmpermanent502W001
no_such_procedurewamp.error.no_such_procedureno_such_procedurepermanent501W002
no_such_registrationwamp.error.no_such_registrationno_such_registrationpermanent502W003
no_such_subscriptionwamp.error.no_such_subscriptionno_such_subscriptionpermanent502W004
no_such_sessionwamp.error.no_such_sessionno_such_sessionpermanent500W005
procedure_already_existswamp.error.procedure_already_existsprocedure_already_existspermanent400W006
option_not_allowedwamp.error.option_not_allowedoption_not_allowedpermanent400W007
disclose_me_not_allowedwamp.error.disclose_me.not_alloweddisclose_me_not_allowedpermanent400W008
no_eligible_calleewamp.error.no_eligible_calleeno_eligible_calleetransient502W009
no_available_calleewamp.error.no_available_calleeno_available_calleetransient502W010
protocol_violationwamp.error.protocol_violationprotocol_violationpermanent400W011
invalid_payloadwamp.error.invalid_payloadinvalid_payloadpermanent400W012
canceledwamp.error.canceledcanceledpermanent400W013
not_in_sessionbondy.error.not_in_sessionnot_in_sessionpermanent400W014
deprecated_procedurebondy.error.deprecated_proceduredeprecated_procedurepermanent410W015
feature_not_supportedwamp.error.feature_not_supportedfeature_not_supportedpermanent501W016

Cluster ​

TypeURICodeNatureHTTPHandle
node_downbondy.error.node_downnode_downtransient503K001
cluster_not_formedbondy.error.cluster_not_formedcluster_not_formedtransient503K002
partition_detectedbondy.error.partition_detectedpartition_detectedtransient503K003
cluster_disabledbondy.error.cluster_disabledcluster_disabledpermanent501K004

Mail ​

TypeURICodeNatureHTTPHandle
mail_not_configuredbondy.error.mail_not_configuredmail_not_configuredpermanent501M001
no_such_relaybondy.error.no_such_relayno_such_relaypermanent400M002
relay_not_permittedbondy.error.relay_not_permittedrelay_not_permittedpermanent403M003
sender_not_permittedbondy.error.sender_not_permittedsender_not_permittedpermanent403M004
invalid_recipientbondy.error.invalid_recipientinvalid_recipientpermanent400M005
mail_rejectedbondy.error.mail_rejectedmail_rejectedpermanent400M006
mail_delivery_failedbondy.error.mail_delivery_failedmail_delivery_failedtransient502M007
relay_unavailablebondy.error.relay_unavailablerelay_unavailabletransient503M008
mail_queue_fullbondy.error.mail_queue_fullmail_queue_fulltransient429M009

A mail relay is operator-owned infrastructure, and none of these errors describes it. The payload carries the relay's configured name — which the caller supplied, or could read from bondy.mail.relay.list — and nothing else. No hostname, no username, no credential.

The text of an SMTP reply never reaches a caller either. Only the three-digit reply code survives, because a relay banner is written by someone other than Bondy and may quote the recipient, the subject, or anything else its operator chose to put there.

mail_rejected is 400 and not 502: the relay was reachable and working, and declined this particular message, so a gateway status would invite a retry that cannot succeed.

System ​

TypeURICodeNatureHTTPHandle
internal_errorbondy.error.internal_errorinternal_errortransient500S001
unknown_errorbondy.error.unknown_errorunknown_errortransient500S002
service_unavailablewamp.error.unavailableunavailabletransient503S003
unavailablebondy.error.unavailableunavailabletransient503S004
temporarily_unavailablebondy.error.temporarily_unavailabletemporarily_unavailabletransient503S005
gateway_timeoutbondy.error.gateway_timeoutgateway_timeouttransient504S006
bad_gatewaybondy.error.bad_gatewaybad_gatewaytransient502S007
disk_fullbondy.error.disk_fulldisk_fulltransient500S008
out_of_memorybondy.error.out_of_memoryout_of_memorytransient500S009
too_many_connectionsbondy.error.too_many_connectionstoo_many_connectionstransient503S010
too_many_processesbondy.error.too_many_processestoo_many_processestransient500S011
insufficient_resourcesbondy.error.insufficient_resourcesinsufficient_resourcestransient503S012
system_shutdownwamp.close.system_shutdownsystem_shutdowntransient500S013
noprocwamp.error.unavailableunavailabletransient503S014 †
overloadbondy.error.too_many_requeststoo_many_requeststransient429S015 †
overloadedbondy.error.too_many_requeststoo_many_requeststransient429S016 †

POSIX errors ​

Errors named by a POSIX atom — enoent, econnrefused, etimedout and the rest — are not listed. They resolve to bondy.error.<name>, carry the message the runtime gives them, are always transient, and use the handle P-<name>.