Error Handling
Every failed request from the MAGMA SDK rejects with a typed MagmaError. This
page covers the error shape, how to branch on status, the retry/backoff policy
(which is deliberately GET-only), and request timeouts.
MagmaError
MagmaError extends the native Error with two extra fields:
class MagmaError extends Error {
name: 'MagmaError';
message: string; // human-readable error message
code: string; // machine-readable code, e.g. 'NARRATIVE_NOT_FOUND'
status: number; // HTTP status code, e.g. 404
}
It is a named export, so you can use instanceof to narrow:
import { MagmaClient, MagmaError } from '@magma-protocol/sdk';
try {
const narrative = await magma.narratives.get('<NARRATIVE_ID>');
} catch (err) {
if (err instanceof MagmaError) {
console.error(`[${err.status}] ${err.code}: ${err.message}`);
} else {
throw err; // not an API error (e.g. a network or abort error)
}
}
How the error is constructed
When a response is not ok, the SDK attempts to parse the JSON error body
(MagmaAPIError — { error, code, status }) and builds the MagmaError from it.
If the body cannot be parsed as JSON, it falls back to the response's status text
with code HTTP_ERROR. A missing code defaults to 'UNKNOWN'.
Branching on status
status mirrors the HTTP status code. Common cases:
| Status | Typical code | Meaning | What to do |
|---|---|---|---|
401 | UNAUTHORIZED | Missing or expired Privy JWT | Refresh the token, then setAuth(...). |
403 | FORBIDDEN | Valid token, insufficient permissions | Surface to the user; do not retry. |
404 | NARRATIVE_NOT_FOUND / USER_NOT_FOUND | Resource does not exist | Handle as empty/not-found. |
402 | — | Payment required (x402-gated endpoint) | Inject a payment-wrapped fetch — see x402 Payments. |
429 | RATE_LIMITED | Too many requests | Back off and retry later. |
5xx | INTERNAL_ERROR | Server-side error | Auto-retried for GET only (see below). |
try {
await magma.users.getHistory('<WALLET>');
} catch (err) {
if (err instanceof MagmaError && err.status === 401) {
const fresh = await refreshPrivyToken();
magma.setAuth(fresh);
// ...optionally retry the call
}
}
Any MagmaError with status < 500 (e.g. 401, 403, 404, 429) is thrown
immediately and is never retried, regardless of the method.
Retry and backoff (GET-only)
Auto-retry is restricted to idempotent GET requests. Non-GET methods
(POST, PUT, DELETE) move money or mutate state — for example
narratives.back(), prepareMint(), and
challenge() — and are never replayed, so a network blip or transient 5xx
cannot double-submit a transaction.
What is retried, and when:
- Method: only
GET.POST/PUT/DELETEsurface the error on the first failure. - Triggers: a thrown network error, or a
MagmaErrorwithstatus >= 500. AMagmaErrorwithstatus < 500is thrown immediately. - Attempts: controlled by the
retriesoption (default3). With the default, a GET is attempted up to four times total (the initial try plus three retries). - Backoff: exponential,
1000 * 2^attemptms between attempts — roughly 1s, 2s, 4s for the default three retries. After the last attempt the final error is thrown.
// More aggressive retries for a flaky network (GET calls only)
const magma = new MagmaClient({ retries: 5 });
Because retries are scoped to GET, the SDK never needs idempotency keys for
write calls — those simply fail fast and let you decide how to proceed.
Timeouts and abort
Each request (including each retry attempt) is bounded by an AbortController. If
it does not complete within timeout milliseconds (default 10000), the request
is aborted.
- The timer is created per attempt and is always cleared in a
finallyblock, so it never fires on a success, error, or retry path. - An aborted request rejects. For a
GET, the abort is treated like any other network error and is subject to the retry policy above; on the final attempt (or for a non-GETmethod) the abort error propagates to yourcatch.
// Shorter timeout for latency-sensitive paths
const magma = new MagmaClient({ timeout: 3_000 });
An abort surfaces as the underlying runtime error (e.g. an AbortError), not
as a MagmaError — MagmaError is reserved for non-ok HTTP responses. Guard for
both when you need to distinguish:
try {
await magma.narratives.feed();
} catch (err) {
if (err instanceof MagmaError) {
// HTTP error from the API
} else {
// network failure or timeout/abort
}
}
See also
- Installation —
timeoutandretriesoptions. - TypeScript client reference — which methods are
GETvsPOST/PUT. - x402 Payments — handling
402 Payment Required.