Errors & retries
Typed errors
A non-2xx response maps to a typed error carrying the HTTP status, the API's
error code (integer), the message, and the request URL:
| HTTP | Error type |
|---|---|
| 400 / 422 | ValidationError |
| 401 | UnauthorizedError |
| 403 | ForbiddenError |
| 404 | NotFoundError |
| 409 | ConflictError |
| 429 | RateLimitError |
| 5xx | ServerError |
All extend a common base (BoostSpaceApiError / APIError), so you can catch
broadly or narrowly:
from boostspace import NotFoundError, BoostSpaceApiError
try:
bs.spaces.ref(999999).get()
except NotFoundError:
...
except BoostSpaceApiError as e:
print(e.http_status, e.code, e.message)
try { await bs.spaces.ref(999999).get(); }
catch (e) { if (e instanceof NotFoundError) { /* … */ } }
_, err := bs.Spaces.Ref(999999).Get(ctx)
var nf *boostspace.NotFoundError
if errors.As(err, &nf) { /* … */ }
> The error code is an integer — the HTTP status for generic errors, or a
> specific internal code (e.g. 4002 missing field, 4005 not found, 4031 2FA
> required) for finer cases. Don't compare it to strings.
Retries
The runtime retries automatically, but only when it is safe:
- 429 — always retried (rejected before processing), honouring
Retry-After. - 5xx / network errors — retried only for idempotent methods (GET/PUT/DELETE/HEAD/OPTIONS). A POST is never retried, so you can't get a duplicate create.
-
Backoff is exponential with jitter;
maxRetriesdefaults to 3 and is configurable at construction.
Observability hooks (onRequest/onResponse/onError) fire once per logical
request (retries are internal) and can inject headers (e.g. trace context).