Error handling
The basics are on the Errors reference page. This guide is the production-shaped version: which errors are terminal, which to retry, and what to log.
Decision tree
status code ──► retry?
─────────────────────
4xx (except 429) no — fix and re-call
429 yes — backoff (or honour Retry-After)
5xx yes — backoff
network error yes — backoff (max 3 attempts)
The SDKs already implement this — you only need to write retry logic when you’ve explicitly disabled it (retries=0).
Distinguishing transient vs terminal in TypeScript
import {
BadRequestError,
AuthenticationError,
InsufficientCreditsError,
NotFoundError,
RateLimitError,
ServiceUnavailableError,
NetworkError,
} from "brightbean";
try {
await call();
} catch (err) {
if (err instanceof BadRequestError || err instanceof NotFoundError) {
// terminal: log + drop
logger.error({ requestId: err.requestId, code: err.code }, "bad request");
} else if (err instanceof AuthenticationError) {
// terminal: rotate key, page on-call
pager.alert("brightbean auth failure", { requestId: err.requestId });
} else if (err instanceof InsufficientCreditsError) {
// terminal for THIS call; halt batch and top up
queue.pauseAll("out of credits");
} else if (err instanceof RateLimitError) {
// already retried by SDK — escalate if you see this
metrics.increment("brightbean.rate_limited.unrecovered");
} else if (err instanceof ServiceUnavailableError || err instanceof NetworkError) {
// already retried — likely an outage
metrics.increment("brightbean.upstream_down");
} else {
throw err; // unexpected
}
}
What to log
Always log:
request_id(the one the SDK pulled off the response). Engineering support starts here.codefrom the error envelope.- Your own correlation ID (which user/job triggered this).
Never log:
- The full request body. It may contain titles or URLs you don’t want in your logs.
- The API key. Obviously.
Idempotency for retries you control
If you’re doing your own retries (for whatever reason — disabled SDK retry, or fanning out across processes), pass Idempotency-Key on POSTs. A repeated key within 24h returns the original response and does not consume credits.
import hashlib
key = hashlib.sha256(f"{user_id}:{title}".encode()).hexdigest()
client.score(title=title, idempotency_key=key)
Surfacing errors to end users
In an app:
| BrightBean error | What the user sees |
|---|---|
BadRequestError (e.g. title too long) |
Inline validation: “Title must be under 250 characters.” |
InsufficientCreditsError |
Modal: “You’re out of credits — upgrade or top up.” (Link to billing.) |
RateLimitError (after SDK retries) |
Toast: “We’re processing your previous request — try again in a moment.” |
AuthenticationError |
Server-side problem, not the user’s. Generic “Something went wrong.” + alert. |
ServiceUnavailableError, NetworkError |
“BrightBean is temporarily unavailable. Retry?” |
Debugging a single failed call
Every error carries request_id. Forward it to support@brightbean.xyz along with the timestamp and we can look up the server-side trace.