Skip to main content

braze_sync/braze/
error.rs

1//! Braze API error variants. See IMPLEMENTATION.md §6.7 / §8.
2
3use reqwest::StatusCode;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
7pub enum BrazeApiError {
8    /// Non-success HTTP status that wasn't already mapped to a typed
9    /// variant below. Carries the body so users have something to grep.
10    #[error("HTTP {status}: {body}")]
11    Http { status: StatusCode, body: String },
12
13    /// Network / decode errors from `reqwest`. Includes JSON parse errors
14    /// (reqwest::Error::is_decode) — the message will say so.
15    #[error("network error: {0}")]
16    Network(#[from] reqwest::Error),
17
18    /// 401 from Braze. Almost always a wrong / missing API key.
19    #[error("authentication failed (invalid api key)")]
20    Unauthorized,
21
22    /// 404 mapped from a get-by-name endpoint. Generic Http {404} from a
23    /// list endpoint stays as Http; this variant is reserved for the
24    /// "this specific resource doesn't exist" case so callers can branch
25    /// on it without string matching.
26    #[error("Braze resource not found: {resource}")]
27    NotFound { resource: String },
28
29    /// MAX_RETRIES of 429 in a row. The caller should slow down (or
30    /// raise the configured rate limit) rather than just retrying again.
31    #[error("rate limit retries exhausted")]
32    RateLimitExhausted,
33
34    /// A list endpoint returned a truncated page and v0.2.0 does not yet
35    /// implement pagination. Returned instead of silently dropping the
36    /// missing results, because for content blocks that drop would let
37    /// `apply` create duplicates of blocks living on page 2+ (and
38    /// `--archive-orphans` would miss them entirely).
39    #[error(
40        "Braze {endpoint}: pagination not implemented in v0.2.0 ({detail}); \
41         aborting to prevent duplicate-create or silent orphan loss"
42    )]
43    PaginationNotImplemented {
44        endpoint: &'static str,
45        detail: String,
46    },
47
48    /// Braze returned HTTP 200 with a non-success `message` field that
49    /// does not match any known not-found phrase. Surfaced verbatim so
50    /// an unexpected server-side failure is loud instead of being
51    /// silently misclassified as `NotFound` — the wire shapes in v0.2.0
52    /// are ASSUMED per IMPLEMENTATION.md §8.3, so any unrecognised
53    /// status message is exactly the signal the operator needs to see.
54    #[error("Braze {endpoint}: unexpected message in 200 response: {message}")]
55    UnexpectedApiMessage {
56        endpoint: &'static str,
57        message: String,
58    },
59
60    /// A list endpoint returned two entries sharing the same `name`.
61    /// Braze is expected to enforce name uniqueness for named resources
62    /// (content blocks, email templates), so this is a contract
63    /// violation rather than an operator-fixable condition. We surface
64    /// it loudly because the diff/apply path indexes by name — silently
65    /// keeping only one of a duplicate pair would hide a resource from
66    /// every subsequent list/update/archive operation.
67    #[error("Braze {endpoint}: duplicate name {name:?} in list response")]
68    DuplicateNameInListResponse {
69        endpoint: &'static str,
70        name: String,
71    },
72}