stalwartlabs/stalwart · error · ScimError

Precondition Failed

Error message

Precondition Failed

What it means

A SCIM Error with HTTP status 412 ("Precondition Failed"), built by `Error::precondition_failed()`. It indicates that a conditional request precondition supplied by the client (e.g. an If-Match ETag used for optimistic concurrency) did not match the current state of the resource.

Source

Thrown at crates/scim-proto/src/message/error.rs:179

    pub fn unauthorized() -> Self {
        Error::new(401)
    }

    pub fn forbidden(detail: impl Into<Cow<'static, str>>) -> Self {
        Error::new(403).with_detail(detail)
    }

    pub fn not_found() -> Self {
        Error::new(404)
    }

    pub fn conflict(detail: impl Into<Cow<'static, str>>) -> Self {
        Error::new(409).with_detail(detail)
    }

    pub fn precondition_failed() -> Self {
        Error::new(412)
    }

    pub fn max_operations_exceeded(max_operations: usize) -> Self {
        Error::new(413).with_detail(format!(
            "The number of operations in the bulk request exceeds the maxOperations ({max_operations})."
        ))
    }

    pub fn max_payload_size_exceeded(max_payload_size: usize) -> Self {
        Error::new(413).with_detail(format!(
            "The size of the bulk operation exceeds the maxPayloadSize ({max_payload_size})."
        ))
    }

    pub fn internal_error() -> Self {
        Error::new(500)
    }

View on GitHub (pinned to e962003857)

Solutions

  1. Re-fetch the resource to obtain the current ETag/version, reapply your change, and retry the conditional request.
  2. If the resource changed in ways that invalidate your change, merge manually before retrying.
  3. Only send If-Match when you actually intend compare-and-swap semantics; otherwise omit the precondition.

Example fix

// before
client.put_with_if_match(user.id, stale_etag, user).await?;
// after
let fresh = client.get_user(&user.id).await?; // obtains current etag
client.put_with_if_match(&fresh.id, &fresh.etag, merged_user).await?;
Defensive patterns

Strategy: retry

Try / catch

match client.put_with_if_match(id, &etag, body).await {
    Err(e) if e.status() == 412 => {
        let fresh = client.get_user(&id).await?;
        client.put_with_if_match(&fresh.id, &fresh.etag, merged(&fresh, body)).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: PUT/PATCH/DELETE requests carrying an If-Match header whose ETag no longer matches the resource's current version, so the server responds with `Error::precondition_failed()`.

Common situations: Two admins edit the same user concurrently and one saves after the other; a client caches a resource, the resource changes, then the client performs a conditional update with the stale ETag.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/2b869d9990ff6dd1. Report an issue: GitHub.