pub struct DenialContext {
pub reason: DenialReason,
pub source: Option<String>,
pub attempted: Option<String>,
pub expected: Option<Vec<String>>,
pub hop_index: Option<u8>,
pub cap: Option<u64>,
pub actual: Option<u64>,
}Expand description
Structured machine-parseable companion to error.message for
recoverable denials.
The field is optional and additive on the public error envelope —
every previously-shipped {code, message} envelope remains valid, and
agents that ignore this struct continue to work. When present, it
carries the concrete parameters an LLM agent can use to plan a recovery
(e.g. “the redirect to evil.example.com was denied because it is not
in the crossref allowlist”) without text-mining error.message.
§Wire shape
#[serde(deny_unknown_fields)]: forward-compatible field additions on
the wire are forbidden by design — adding a field to this struct is a
breaking change. This is why the type is not #[non_exhaustive]
(per docs/PUBLIC_API.md §8): both production rules — Rust struct
construction outside the crate AND wire-level extension — must agree.
All fields except reason are optional. Producers populate the fields
relevant to the reason and leave the rest at None; consumers MUST
tolerate any subset of fields being present. Optional fields are
skipped on serialize but accepted as missing on deserialize via
#[serde(default, skip_serializing_if = "Option::is_none")].
Self::expected is Option<Vec<String>> rather than Vec<String>
so the producer can distinguish “this reason has no allowlist channel”
(None → field absent on the wire) from “this is the explicit list of
acceptable values, possibly empty” (Some(vec![]) → "expected":[] on
the wire). The previous Vec<String> shape collapsed both states
into “field omitted”, which an LLM agent could not safely disambiguate.
Mapping table: see ADR-0023 §4, plus the
From<&HttpError> for Option<DenialContext> and
From<&FetchError> for Option<DenialContext> impls in
crate::http / crate::source.
Fields§
§reason: DenialReasonClosed-enum reason code; the only required field.
source: Option<String>Resolver source key (e.g. "crossref") when one is in scope.
attempted: Option<String>Concrete value the producer attempted (host, path, hex magic bytes, scheme prefix). Shape is reason-specific; consumers MUST treat it as opaque text.
expected: Option<Vec<String>>Allowlist entries / acceptable values. Option<Vec<String>> so the
producer can distinguish “this reason has no allowlist channel”
(None, field absent on the wire) from “this is the explicit list
of acceptable values, possibly empty” (Some(vec![]), "expected":[]
on the wire). The inner Vec<String> is used even when only one
value is meaningful (e.g. Some(vec!["%PDF-".into()])) so the
format does not have to flip when multiple values are acceptable.
hop_index: Option<u8>Redirect-chain hop position, 0-indexed. u8 because the chain is
hard-capped at crate::http’s MAX_REDIRECTS (= 10) and any
larger value indicates a bug.
cap: Option<u64>Size or rate cap value (e.g. PDF_MAX_BYTES).
actual: Option<u64>Observed value (e.g. response bytes when Self::cap is the byte
cap, or row schema_version when Self::cap is the binary’s).
Trait Implementations§
Source§impl Clone for DenialContext
impl Clone for DenialContext
Source§fn clone(&self) -> DenialContext
fn clone(&self) -> DenialContext
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for DenialContext
impl Debug for DenialContext
Source§impl<'de> Deserialize<'de> for DenialContext
impl<'de> Deserialize<'de> for DenialContext
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for DenialContext
impl PartialEq for DenialContext
Source§fn eq(&self, other: &DenialContext) -> bool
fn eq(&self, other: &DenialContext) -> bool
self and other values to be equal, and is used by ==.