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}