Resume failed uploads
Uploads are a two-step thing — first the bytes land on Walrus, then a Sui transaction registers them on-chain. The most expensive step (PUT) is content-addressed and idempotent; the cheap step (tx) is where most failures actually happen. The SDK lets you retry just the tx without re-uploading bytes.
Where uploads break
The pipeline:
The slow stage is the publisher PUT. The most failure-prone stage is the register tx — wallet rejections, plan-tier checks, network blips, gas shortage. About 95% of failures hit the register step after PUT succeeded, which means your bytes are already on Walrus.
That's the case worth optimizing.
What you get from the SDK
When register fails after PUT, the SDK throws RegistrationError with a
.checkpoint field that carries everything needed to retry:
InsufficientGasError extends RegistrationError for the most common cause
— wallet out of SUI. Same .checkpoint, but also .requiredMist and
.address so you can surface a faucet prompt.
The recovery pattern
registerOnly is idempotent on success — Walrus storage is paid at PUT
time and survives any number of failed register attempts. Retry as often
as needed.
Persisting the checkpoint
The checkpoint is plain JSON except contentHash, which is a Uint8Array.
Serialize as a number array and reconstitute on load.
Node — filesystem
Browser — localStorage
Server-side — database row
For backend uploads, store the checkpoint as a JSON column on whatever
"upload job" record you have. Keep the JSON shape minimal — blobId is the
primary key you'll be querying on.
What about partial PUT failures?
Less common, but real. If the publisher PUT itself fails partway:
- Network drop mid-PUT — restart from byte 0. Walrus is content-addressed,
so if any of your prior partial PUT actually landed on storage nodes, the
retry hits the
alreadyCertifiedfast path and returns immediately. - Publisher timeout — same, restart from byte 0.
There's no chunked-resume primitive for a single PUT in this SDK. True
chunked resume would require @mysten/walrus's direct-to-node
writeBlobFlow — a heavier dep we deliberately don't pull in. The
content-addressing trick gets you most of the way there for free.
The InsufficientGasError UX
Real-world testing surfaced this as the #1 first-run failure. The SDK detects it specifically:
Common message in your UI:
Your wallet needs ~0.008 SUI to record this upload on-chain. Top up via the testnet faucet and click Retry — your bytes are already on Walrus.
The 0.008 SUI cost is fixed per register tx (regardless of blob size).
Idempotency at the publisher
Worth knowing: re-uploading the same bytes returns the same blob_id and
hits the publisher's alreadyCertified fast path. So even if you lose the
checkpoint, re-running upload() with the same data is safe — you
won't double-pay storage. The publisher just returns the existing blob_id
in milliseconds.
This is why the SDK can guarantee blobId is stable across retries even
without a checkpoint — Walrus content-addressing does the deduplication.
The "checkpoint" is really just a memo to your application that you've already paid for storage. The bytes themselves are findable by re-PUTing or by knowing the blob_id. Lose the checkpoint, you'll re-PUT (cheap); keep it, you skip straight to register (cheaper).