Skip to main content

SetError

Struct SetError 

Source
#[non_exhaustive]
pub struct SetError { pub error_type: SetErrorType, pub description: Option<String>, pub properties: Option<Vec<String>>, pub existing_id: Option<Id>, pub max_recipients: Option<u64>, pub invalid_recipients: Option<Vec<String>>, pub not_found: Option<Vec<Id>>, pub max_size: Option<u64>, pub extra: Map<String, Value>, }
Expand description

JMAP wire-format SetError; emitted by TasksBackend write methods wrapped as BackendSetError::SetError and serialised into notCreated / notUpdated / notDestroyed maps by the /set handlers (RFC 8620 §5.3). A per-item error in a /set response (notCreated, notUpdated, notDestroyed maps) (RFC 8620 §5.3).

Construct with SetError::new and chain the builder methods as needed.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§error_type: SetErrorType

The machine-readable error type.

§description: Option<String>

Optional human-readable description of the error.

§properties: Option<Vec<String>>

Property names that caused the error (for invalidProperties).

§existing_id: Option<Id>

The existing object id (for alreadyExists — RFC 8621 §5.7).

§max_recipients: Option<u64>

Maximum recipients allowed (for tooManyRecipients — RFC 8621 §7.5).

§invalid_recipients: Option<Vec<String>>

Invalid recipient addresses (for invalidRecipients — RFC 8621 §7.5).

§not_found: Option<Vec<Id>>

Missing blob IDs (for blobNotFound — RFC 8621 §5.5).

§max_size: Option<u64>

Maximum message size in octets (for tooLarge on EmailSubmission — RFC 8621 §7.5).

§extra: Map<String, Value>

Catch-all for extension-defined SetError fields not covered by the typed members above.

JMAP extensions sometimes ship error variants whose wire shape includes additional structured fields beyond the RFC 8620 §5.3 base set — e.g. JMAP Chat’s rateLimited SetError carries a serverRetryAfter UTCDate telling the client when it may retry, and mdnAlreadySent (RFC 8621 §7.7) is a typed extension error variant. This map preserves any such field across serialize / deserialize round-trip, mirroring the extras-preservation policy on the client-side jmap_types::SetError type.

Use SetError::with_extra to populate from handler code:

SetError::new(SetErrorType::custom("rateLimited"))
    .with_description("Slow mode is active for this chat")
    .with_extra("serverRetryAfter", json!(retry_after_str))

Per workspace AGENTS.md “Extras-preservation policy” — wire format is byte-identical to a pre-extras SetError when the map is empty (the skip_serializing_if collapses it).

§Reserved-name invariant (bd:JMAP-jfia.17)

Keys in RESERVED_SET_ERROR_WIRE_NAMES MUST NOT appear in this map. The typed fields above serialize to those names, so a colliding extras key produces a JSON object with two keys at the same level — RFC 8259 §4 permits duplicate keys but the behaviour is implementation-defined and the resulting SetError is malformed in practice.

SetError::with_extra enforces this in debug builds via debug_assert!; direct field mutation (this field is pub per the workspace extras-preservation policy) bypasses that guard. Test and audit code SHOULD call SetError::validate_extras to detect collisions deterministically across build profiles.

Implementations§

Source§

impl SetError

Source

pub fn new(error_type: SetErrorType) -> SetError

Construct a SetError with the given type and all optional fields None.

Source

pub fn with_description(self, desc: impl Into<String>) -> SetError

Set the human-readable description.

§Security

SetError.description is serialized verbatim into the JMAP wire response (RFC 8620 §5.3 notCreated / notUpdated / notDestroyed maps) and is visible to any client that can dispatch the failing /set call. The MUST-NOT rules that apply to JmapBackend::Error’s Display output also apply to this string:

  • Credential material — auth tokens, passwords, push verification codes, invite codes, session cookies, or anything derived byte-for-byte from an Authorization-header value.
  • Blob content — email bodies, sieve scripts, file contents, or any user-supplied opaque payload.
  • PII shaped like an email address in any code path that an unauthenticated caller can trigger.

Wrap downstream errors with crate::server_fail_from_backend (which always emits the static “internal error” description) rather than interpolating them into a SetError description.

SetError paths are MORE leak-prone than serverFail because adversarial clients can probe for descriptions by sending crafted /set arguments — the typed-error contract guarantees the response includes a SetError for every failing target. Static, caller-meaningful descriptions (“rate limit exceeded — retry in N seconds”, “patch nesting exceeds server limit”) are fine; backend-error interpolations are not.

Precedent: the parallel contract on JmapBackend::Error (bd:JMAP-sc1b.100) and the matching handler-side leak path closed in bd:JMAP-wlip.2. This warning added in bd:JMAP-wlip.26.

Source

pub fn with_properties<I, S>(self, props: I) -> SetError
where I: IntoIterator<Item = S>, S: Into<String>,

Set the list of property names that caused the error.

Source

pub fn with_existing_id(self, id: Id) -> SetError

Set the existing object id (used with alreadyExists).

Source

pub fn with_max_recipients(self, n: u64) -> SetError

Set the maximum recipients (used with tooManyRecipients — RFC 8621 §7.5).

Source

pub fn with_invalid_recipients<I, S>(self, addrs: I) -> SetError
where I: IntoIterator<Item = S>, S: Into<String>,

Set the invalid recipient addresses (used with invalidRecipients — RFC 8621 §7.5).

Source

pub fn with_not_found(self, ids: Vec<Id>) -> SetError

Set the missing blob IDs (used with blobNotFound — RFC 8621 §5.5).

Source

pub fn with_max_size(self, n: u64) -> SetError

Set the maximum message size in octets (used with tooLarge on EmailSubmission — RFC 8621 §7.5).

Source

pub fn with_extra(self, key: impl Into<String>, value: Value) -> SetError

Insert an extension-defined field into Self::extra.

Used by handlers to attach typed wire fields that no with_* builder covers — for example JMAP Chat’s rateLimited SetError must carry a serverRetryAfter UTCDate:

SetError::new(SetErrorType::custom("rateLimited"))
    .with_description("Slow mode is active for this chat")
    .with_extra("serverRetryAfter", serde_json::json!(retry_after_str))

The serialized wire shape merges key/value at the same level as the typed fields (via #[serde(flatten)] on Self::extra). Calling with_extra("type", ...), with_extra("properties", ...), or any other reserved wire-name will produce a malformed SetError on the wire — callers are responsible for choosing extension-namespace keys that do not collide with the typed-field wire names.

In debug builds, a with_extra(key, ...) call where key is in the reserved set RESERVED_SET_ERROR_WIRE_NAMES panics via debug_assert! to catch the bug at first test run (bd:JMAP-wlip.3). Release builds preserve the current no-validation behaviour to avoid silent runtime cost on a correctly-written caller.

Source

pub fn validate_extras(&self) -> Result<(), ReservedExtrasKey>

Validate that Self::extra does not contain any key in RESERVED_SET_ERROR_WIRE_NAMES (bd:JMAP-jfia.17).

Self::with_extra enforces the same invariant in debug builds via debug_assert!, but direct field mutation (e.g. err.extra.insert("type", json!("evil"))) bypasses that guard. This method is the deterministic, build-profile-independent gate: callers and tests that construct SetError values programmatically should run it before serializing, to catch the collision case that would produce a malformed wire shape with two keys at the same name.

Returns the first colliding key on Err; check validate_extras in a loop if you need to surface all collisions.

§Errors

Returns ReservedExtrasKey with the first reserved key encountered in Self::extra.

Trait Implementations§

Source§

impl Clone for SetError

Source§

fn clone(&self) -> SetError

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for SetError

Source§

fn deserialize<__D>( __deserializer: __D, ) -> Result<SetError, <__D as Deserializer<'de>>::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for SetError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<E> From<SetError> for BackendSetError<E>

Source§

fn from(e: SetError) -> BackendSetError<E>

Converts to this type from the input type.
Source§

impl PartialEq for SetError

Source§

fn eq(&self, other: &SetError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for SetError

Source§

fn serialize<__S>( &self, __serializer: __S, ) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for SetError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.