status & errors — the cross-cutting result taxonomy¶
In one paragraph
Two closed sets, one per direction. tr::graph::status_t is what a local
call answers with: every read / write / await and every field operation
returns tr::graph::result_t<T>, which is std::expected<T, status_t>.
tr::wire::err_t is what a peer is told: a registered u16 code carried in
the ERROR TLV, plus registry-side severity and disposition that never travel on
the wire. Neither set is extensible — an application’s own failures are
application data, not protocol errors.
What it does¶
Every other module on this site depends on this one, and nothing here depends on
anything else: the interface map draws status as the sink
of the dependency graph. That is the whole point of keeping it separate — the
codec, the graph and the routing plane can all name a failure without agreeing on
anything else.
The two taxonomies answer different questions:
status_tanswers a caller. It is the L4 control-surface code: the path did not resolve, the payload’s type does not fit the vertex, the queue is full, the await deadline expired, the ACL refused. It is returned by value, never thrown, which is what lets the whole runtime build with-fno-exceptions.err_tanswers a peer. It is the frozentr::<concept>::<error>registry: a stable u16 that identifies the failure across implementations and languages, so a node written in Rust and a node written in C++ report a truncated frame with the same bytes.err_severity_tanderr_disposition_tare registry-side metadata — how bad, and whether a retry could help — and are deliberately not on the wire, so a receiver’s policy can differ without a protocol change.
Both sets are closed. An application that wants to report “the sensor is unplugged” writes that into a vertex; it does not mint a protocol error. The wire codes and their meanings are specified in reference §protocol TLVs §error codes; this page is the C++ surface for them.
Interface¶
A local call, a peer-visible failure, and the conversion between the two:
namespace tr::graph {
enum class status_t { NOT_FOUND, INVALID_PATH, TYPE_MISMATCH, /* … */ };
template <class T> using result_t = std::expected<T, status_t>;
constexpr const char* to_string(status_t) noexcept;
}
namespace tr::wire {
enum class err_t : std::uint16_t { FRAME_TRUNCATED = 0x0001, /* … */ };
constexpr std::string_view err_path(err_t) noexcept; // "tr::frame::truncated"
constexpr err_severity_t err_severity(err_t) noexcept;
constexpr err_disposition_t err_disposition(err_t) noexcept;
}
err_path is the bridge to the human-readable registry name — the same
tr::<concept>::<error> string the reference suite and the Wireshark dissector
use, so a log line and a spec section can be matched by eye.
Checksums¶
The frame trailer’s integrity codes live beside the taxonomy because they are the
other thing every layer needs and no layer owns: CRC-32C (Castagnoli) for the
32-bit trailer and CRC-16-CCITT for the narrow one. Both are incremental — a
state object fed span by span — because a rope is decoded link by link and the
CRC must cover bytes that are never contiguous in memory. Which one a frame
carries is an opt-bit decision described in
frame-codec and specified in
reference §data format.
Pitfalls¶
status_tis noterr_trenamed. They overlap in meaning, not in membership:BACKPRESSUREis a local answer,FLOW_BACKPRESSUREis a wire code, and a forwarder that maps one to the other does so explicitly. Do not cast between them.Severity and disposition are receiver policy, not protocol. They are compiled into the registry so every node agrees on a default reading; they are not transmitted, so a node is free to treat a
TRANSIENTcode as fatal.result_t<void>is the success-only shape. A call that returns nothing on success still returns aresult_t— discarding it discards the failure.A link that did not come up is
TRANSPORT_DOWN, notNOT_FOUND. The two read alike locally and diverge on the wire:NOT_FOUNDbecomestr::path::not_found, whose registry disposition is PERMANENT, whiletr::transport::downis TRANSIENT. SpendingNOT_FOUNDon a refused dial or a listener that could not bind therefore tells a correct peer to stop retrying a link that would have come back — which is what the built-in transport factories did until #929.
API reference¶
-
enum class tr::graph::status_t¶
The L4 control-surface error code — a closed, protocol-only set (ADR-0010).
The read/write/await surface returns
std::expected<T, status_t>. These names mirror the documented protocol error codes (docs/reference/05 §error codes) and reconcile with thetr::error namespace when RFC-0002 lands. Not for user error codes.Values:
-
enumerator NOT_FOUND¶
Path doesn’t resolve / no last-known-value.
-
enumerator INVALID_PATH¶
Malformed path or non-UTF-8 NAME segment.
-
enumerator TYPE_MISMATCH¶
Payload type incompatible with the vertex/field.
-
enumerator BACKPRESSURE¶
Queue full / dispatch-depth cap hit.
-
enumerator TIMEOUT¶
Await deadline expired.
-
enumerator SCHEMA_NOT_FOUND¶
Field read/write on a vertex that doesn’t expose it.
-
enumerator PERMISSION_DENIED¶
ACL rejected (ALLOW-only ACEs + INHERIT, core subset).
-
enumerator PATH_IN_USE¶
Registration collided with an existing vertex.
-
enumerator TRANSPORT_DOWN¶
A link could not be brought up — a dial, bind, or handshake that failed.
The transport-plane counterpart of
NOT_FOUND, and deliberately NOT the same member:NOT_FOUNDsays this address does not resolve, which the wire registry reads as PERMANENT, while a refused connect or a listener that could not bind is TRANSIENT — retrying it may succeed (wire::err_t::TRANSPORT_DOWN,wire::err_disposition_t). Collapsing the two told a peer to stop retrying a link that would have come back (#929).
-
enumerator NOT_FOUND¶
-
template<class T>
using tr::graph::result_t = std::expected<T, status_t>¶ The L4 result type:
std::expected<T, status_t>.Success is the value side of the expected; an empty STATUS=OK on the wire maps to a
result_twith a value (orresult_t<void>success).
-
constexpr const char *tr::graph::to_string(status_t s) noexcept¶
The stable lower-case wire name of a
status_t(e.g."not_found").Structurally exhaustive, and deliberately so (#876): the switch carries neither a
default:label nor a fall-through tail, so-Wswitch— an error under the-Werror=switchthe library compiles with — names this switch the moment an enumerator is added tostatus_twithout a name here. The retiredreturn "unknown";tail turned that into a nameless status at runtime, the reporting twin of the wire mislabel theerror_codebridge carried.Warning
smust be astatus_tenumerator. Every status the library produces is one — a status is minted from the enumerators and is never cast in from wire bytes (the wire’s own registry iswire::err_t) — so the end of this function is unreachable by construction, and a call that reaches it in a constant expression is a compile error.
-
enum class tr::wire::err_t : std::uint16_t¶
Registered protocol error codes (RFC-0002 §D) — the u16 wire identity of each built-in
tr::…error path, carried LE in the ERROR TLV’s first-child VALUE.Values:
-
enumerator FRAME_TRUNCATED¶
tr::frame::truncated
-
enumerator FRAME_INVALID¶
tr::frame::invalid
-
enumerator FRAME_CRC_FAIL¶
tr::frame::crc_fail
-
enumerator TLV_NESTING_TOO_DEEP¶
tr::tlv::nesting_too_deep— exceeds this RECEIVER’s decode resources (RFC-0006; depth is resource-bounded, never a constant).
-
enumerator PATH_NOT_FOUND¶
tr::path::not_found
-
enumerator PATH_INVALID¶
tr::path::invalid
-
enumerator PATH_IN_USE¶
tr::path::in_use
-
enumerator SCHEMA_TYPE_MISMATCH¶
tr::schema::type_mismatch
-
enumerator SCHEMA_NOT_FOUND¶
tr::schema::not_found
-
enumerator FLOW_BACKPRESSURE¶
tr::flow::backpressure
-
enumerator FLOW_TIMEOUT¶
tr::flow::timeout
-
enumerator FLOW_ADDRESS_SHIFT_GAP¶
tr::flow::address_shift_gap
-
enumerator ACCESS_DENIED¶
tr::access::denied
-
enumerator TRANSPORT_DOWN¶
tr::transport::down
-
enumerator VERSION_MISMATCH¶
tr::version::mismatch
-
enumerator FRAME_TRUNCATED¶
-
enum class tr::wire::err_severity_t : std::uint8_t¶
Registry-side severity of a protocol error (RFC-0002 §D) — advisory metadata that never travels on the wire.
Values:
-
enumerator WARN¶
Expected-in-operation outcome (e.g. a missing path).
-
enumerator ERROR¶
A genuine protocol-level failure.
-
enumerator CRITICAL¶
The peer relationship itself is unsound.
-
enumerator WARN¶
-
enum class tr::wire::err_disposition_t : std::uint8_t¶
Registry-side disposition of a protocol error (RFC-0002 §D) — what a caller should do next; never travels on the wire.
Values:
-
enumerator TRANSIENT¶
Retry may succeed (congestion, timing, lossy link).
-
enumerator PERMANENT¶
Don’t retry this request as-is.
-
enumerator FATAL¶
Tear down the peer relationship.
-
enumerator TRANSIENT¶
-
constexpr std::string_view tr::wire::err_path(err_t e) noexcept¶
The canonical
tr::…namespace path of a registered error code (RFC-0002 §A/§D) — the string identity a NAME-form ERROR would carry.- Parameters:
e – the registered error code
- Returns:
the frozen
tr::<concept>::<error>path (empty for an unregistered value)
-
constexpr err_severity_t tr::wire::err_severity(err_t e) noexcept¶
The registry severity of a registered error code (RFC-0002 §D).
- Parameters:
e – the registered error code
- Returns:
WARN/ERROR/CRITICALper the frozen registry table
-
constexpr err_disposition_t tr::wire::err_disposition(err_t e) noexcept¶
The registry disposition of a registered error code (RFC-0002 §D).
- Parameters:
e – the registered error code
- Returns:
TRANSIENT/PERMANENT/FATALper the frozen registry table
-
struct crc32c_state¶
A running CRC-32C accumulator — the one home of the init/final-xor constants.
Feed the covered bytes as any number of contiguous chunks (a rope crossing link boundaries, a payload-plus-trailer region), then read
value(): byte-identical tocrc32cover the concatenation (the CRC is associative over the feed), with no intermediate buffer. The single-/two-spancrc32cbelow delegate to it.Public Functions
-
inline constexpr void feed(std::span<const std::byte> data) noexcept¶
Feed one contiguous chunk of covered bytes.
-
inline constexpr std::uint32_t value() const noexcept¶
The finalized CRC-32C over everything fed so far.
Public Members
-
std::uint32_t c = 0xFFFFFFFFu¶
Running state; init per RFC 3720.
-
inline constexpr void feed(std::span<const std::byte> data) noexcept¶
-
struct crc16_ccitt_state¶
A running CRC-16-CCITT (FALSE) accumulator — the crc16 twin of crc32c_state (init 0xFFFF, no final xor). Same feed-chunks-then-read-value contract.
Public Functions
-
inline constexpr void feed(std::span<const std::byte> data) noexcept¶
Feed one contiguous chunk of covered bytes.
-
inline constexpr std::uint16_t value() const noexcept¶
The finalized CRC-16-CCITT over everything fed so far.
Public Members
-
std::uint16_t c = 0xFFFFu¶
Running state; init 0xFFFF, no final xor.
-
inline constexpr void feed(std::span<const std::byte> data) noexcept¶
-
constexpr std::uint32_t tr::crc::crc32c(std::span<const std::byte> data) noexcept¶
CRC-32C (Castagnoli) over
data.
-
constexpr std::uint16_t tr::crc::crc16_ccitt(std::span<const std::byte> data) noexcept¶
CRC-16-CCITT (FALSE) over
data.
See: frame-codec (where the codes are produced),
graph (where result_t is returned),
interface map,
reference §protocol TLVs.