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.
402—Payment 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 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​