Skip to main content

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:

StatusTypical codeMeaningWhat to do
401UNAUTHORIZEDMissing or expired Privy JWTRefresh the token, then setAuth(...).
403FORBIDDENValid token, insufficient permissionsSurface to the user; do not retry.
404NARRATIVE_NOT_FOUND / USER_NOT_FOUNDResource does not existHandle as empty/not-found.
402Payment required (x402-gated endpoint)Inject a payment-wrapped fetch — see x402 Payments.
429RATE_LIMITEDToo many requestsBack off and retry later.
5xxINTERNAL_ERRORServer-side errorAuto-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
}
}
Client errors short-circuit

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 / DELETE surface the error on the first failure.
  • Triggers: a thrown network error, or a MagmaError with status >= 500. A MagmaError with status < 500 is thrown immediately.
  • Attempts: controlled by the retries option (default 3). With the default, a GET is attempted up to four times total (the initial try plus three retries).
  • Backoff: exponential, 1000 * 2^attempt ms 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 });
Idempotency by design

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 finally block, 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-GET method) the abort error propagates to your catch.
// 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 MagmaErrorMagmaError 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