Skip to main content

bb_runtime/errors/
delivery.rs

1//! `DeliveryError`
2//!
3//! Returned by the host-facing `Node::deliver_inbound` /
4//! `deliver_event` / `invoke` entry points when delivery cannot be
5//! enqueued onto the ingress.
6
7use crate::bus::AllocFailReason;
8
9/// Errors surfaced by host-facing delivery methods on `Node`.
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub enum DeliveryError {
12    /// The ingress queue is closed (Node is shutting down).
13    IngressClosed,
14
15    /// `deliver_event` / `invoke` referenced an unknown module name.
16    UnknownModule(String),
17
18    /// `deliver_event` referenced a module that exists but has no
19    /// such input port.
20    UnknownInput {
21        /// Module name (resolved successfully).
22        module: String,
23        /// Input port name (not found on the module).
24        input: String,
25    },
26
27    /// `deliver_inbound` received bytes that failed
28    /// `EnvelopeCodec::decode_capped` — malformed prost frame,
29    /// schema-version mismatch, or one of the `NodeConfig.envelope_caps`
30    /// limits exceeded.
31    InvalidEnvelope(String),
32
33    /// `deliver_event` / `invoke` payload exceeded the configured
34    /// per-item cap (`NodeConfig::max_app_event_bytes` or
35    /// `NodeConfig::max_invoke_bytes`). A matching
36    /// `InfraEvent::AppIngressError` lands on the bus alongside this
37    /// synchronous return so observers see the per-item rejection.
38    OversizePayload {
39        /// Bytes the caller attempted to admit.
40        byte_count: usize,
41        /// Cap value the boundary enforced.
42        cap: usize,
43    },
44
45    /// `invoke` carried more `(name, bytes)` inputs than the
46    /// configured `NodeConfig::max_invoke_inputs` cap allowed.
47    /// Matching `InfraEvent::AppIngressError` lands on the bus.
48    TooManyInputs {
49        /// Inputs the caller attempted to admit.
50        count: usize,
51        /// Cap value the boundary enforced.
52        cap: usize,
53    },
54
55    /// `deliver_event` / `invoke` could not allocate the
56    /// framework-owned buffer needed to hold the caller's payload,
57    /// either because `Vec::try_reserve_exact` returned
58    /// `TryReserveError` or because admitting the payload would
59    /// exceed `NodeConfig::ingress_byte_budget`. Matching
60    /// `InfraEvent::AppIngressError` lands on the bus.
61    AllocationFailed {
62        /// Bytes the boundary tried to admit.
63        byte_count: usize,
64        /// Why the reservation failed.
65        reason: AllocFailReason,
66    },
67
68    /// Admitting this payload would push the engine over
69    /// `NodeConfig::ingress_byte_budget`. Matching
70    /// `InfraEvent::AppIngressError` lands on the bus.
71    BudgetExceeded {
72        /// Bytes the boundary tried to admit.
73        byte_count: usize,
74        /// Bytes still available under the configured budget at the
75        /// time of the rejection.
76        budget_remaining: usize,
77    },
78}
79
80impl std::fmt::Display for DeliveryError {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        match self {
83            Self::IngressClosed => write!(f, "ingress queue closed"),
84            Self::UnknownModule(name) => write!(f, "unknown module: {name}"),
85            Self::UnknownInput { module, input } => {
86                write!(f, "module {module} has no input port '{input}'")
87            }
88            Self::InvalidEnvelope(detail) => {
89                write!(f, "inbound envelope rejected: {detail}")
90            }
91            Self::OversizePayload { byte_count, cap } => {
92                write!(f, "payload of {byte_count} bytes exceeds cap of {cap}")
93            }
94            Self::TooManyInputs { count, cap } => {
95                write!(f, "{count} inputs exceeds cap of {cap}")
96            }
97            Self::AllocationFailed { byte_count, reason } => match reason {
98                AllocFailReason::HeapExhausted => {
99                    write!(f, "heap exhausted reserving {byte_count} bytes")
100                }
101                AllocFailReason::PerItemCapExceeded { cap } => {
102                    write!(
103                        f,
104                        "per-item cap {cap} rejected payload of {byte_count} bytes"
105                    )
106                }
107            },
108            Self::BudgetExceeded {
109                byte_count,
110                budget_remaining,
111            } => write!(
112                f,
113                "ingress budget exceeded: {byte_count} bytes requested, {budget_remaining} remaining"
114            ),
115        }
116    }
117}
118
119impl std::error::Error for DeliveryError {}
120