Every SDK error extends WaldropError. Narrow on these classes in your
catch blocks — string-matching error messages is brittle and breaks on
upstream changes.
Hierarchy
WaldropError ← base├── BlobStoreNotFoundError ← no BlobStore owned by address├── BlobNotFoundError ← aggregator returned 404├── AggregatorError ← non-2xx from aggregator (non-404)├── DecryptionError ← SEAL decrypt failed├── SealNotInstalledError ← @mysten/seal peer dep missing└── RegistrationError ← bytes on Walrus, register tx failed └── InsufficientGasError ← wallet out of SUI for gas
Sample catch
import { WaldropError, RegistrationError, InsufficientGasError, BlobStoreNotFoundError,} from "@waldrop/sdk";try { await client.blob.upload({ … });} catch (err) { if (err instanceof InsufficientGasError) { // wallet needs more SUI — show faucet prompt showFaucetPrompt(err.address, err.requiredMist); } else if (err instanceof RegistrationError) { // bytes on Walrus already — retry register only await client.blob.registerOnly({ checkpoint: err.checkpoint, /* … */ }); } else if (err instanceof BlobStoreNotFoundError) { // user hasn't stored anything yet } else if (err instanceof WaldropError) { console.error("SDK error:", err.message, err.cause); } else { throw err; }}
Base class. All SDK errors set err.name to their class name so log
formatters can pick them out without instanceof checks.
BlobStoreNotFoundError
class BlobStoreNotFoundError extends WaldropError { readonly owner: string; // the queried address}
Thrown by client.blob.list, .getStore, and .listViewers when the queried
address has never created a BlobStore. Treat as an empty state, not a failure.
BlobNotFoundError
class BlobNotFoundError extends WaldropError { readonly blobId: string;}
The aggregator returned 404 for client.blob.fetch. The blob id might be
malformed, or the blob's storage reservation expired and was garbage-collected.
AggregatorError
class AggregatorError extends WaldropError { readonly status: number; readonly bodyPreview: string; // first 200 chars of response body}
Non-404 non-2xx from the aggregator (5xx, network, malformed response).
DecryptionError
class DecryptionError extends WaldropError {}
client.crypto.decrypt failed. Usually one of: wrong blobStoreId, the
caller isn't an allowlisted viewer, or the bytes aren't valid SEAL ciphertext.
SealNotInstalledError
class SealNotInstalledError extends WaldropError {}
The optional @mysten/seal peer dep isn't installed but you called
client.crypto.encrypt or .decrypt. Install it:
npm install @mysten/seal
RegistrationError
class RegistrationError extends WaldropError { readonly checkpoint: UploadCheckpoint; // pass to registerOnly to retry}
The publisher PUT succeeded — your bytes are on Walrus, storage tokens are
spent — but the on-chain register_blob transaction failed. The checkpoint
field carries everything client.blob.registerOnly needs to finish without
re-uploading.
class InsufficientGasError extends RegistrationError { readonly requiredMist: number | null; // gas budget the tx wanted readonly address: string | null; // wallet that needs topping up}
A specialization of RegistrationError for the most common cause: wallet
doesn't have enough SUI to pay gas. Catch it explicitly to render a "top up
your wallet" UI before falling through to the generic registration-failed
handler.
Both carry .checkpoint
InsufficientGasError inherits .checkpoint from RegistrationError —
the resume path is identical, the typed subclass exists purely for UX.