Error Handling
Handle Walrus errors in your applications by retrying transient failures with exponential backoff, verifying onchain state after timeouts, and using strict consistency checks for critical data. For a reference of specific error messages and their solutions, see Troubleshooting Common Errors.
Which errors can you retry?
Not all errors are equal. Some are safe to retry, some require you to check onchain state (an operation might have succeeded onchain even though the client did not receive a confirmation), and some require you to fix your input before trying again. The following table summarizes the most common failure types:
| Failure type | Retryable | Check onchain state | Recommended action |
|---|---|---|---|
| Network timeout | Yes | Yes | Retry with exponential backoff |
| Publisher or aggregator down | Yes | No | Try a different endpoint or use the CLI |
| Insufficient funds | No | No | Add SUI or WAL to your wallet |
| Blob too large for memory | No | No | Split the blob into smaller chunks |
| Transaction timeout | Conditional | Yes (critical) | Verify the transaction succeeded before retrying |
| Invalid blob ID | No | No | Fix the blob ID format |
| Blob not found | No | Yes | Verify the blob exists and has not expired |
For transaction timeouts, always check onchain state before retrying. A timed-out transaction might have succeeded. Retrying without checking can result in duplicate blob registrations and wasted funds.
How to retry with exponential backoff
Transient errors such as network timeouts and temporary node unavailability are common in distributed systems. Use exponential backoff to avoid overwhelming the network with retries. The following example waits 2^attempt seconds after each failed attempt, so the default of five attempts waits 2, 4, 8, and 16 seconds between attempts.
async function retryWithBackoff<T>(
operation: () => Promise<T>,
maxRetries: number = 5,
): Promise<T> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const isRetryable =
message.includes('timeout') || message.includes('network');
if (isRetryable && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Attempt ${attempt}/${maxRetries} failed, retrying in ${delay}ms`);
await new Promise((resolve) => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
Use this wrapper for any Walrus operation that might encounter transient failures:
const result = await retryWithBackoff(() =>
client.walrus.writeBlob({ blob: data, epochs: 5, signer }),
);
Only retry errors that are transient. Errors such as insufficient funds, invalid blob IDs, or blobs that are too large do not benefit from retries.
How to verify onchain state after a timeout
When an upload or transaction times out, the operation might have succeeded onchain even though the client did not receive a confirmation. Always check before retrying.
Using the CLI:
$ sui client tx-block TRANSACTION_ID
Using the Walrus CLI:
$ walrus blob-status --blob-id BLOB_ID
If the transaction succeeded, proceed normally. If the transaction does not appear, the network might have dropped it from the mempool, and it is safe to retry.
Capture storage node errors with the TypeScript SDK
When using the Walrus TypeScript SDK, you can capture individual storage node errors during uploads by passing the onError option. This is useful for debugging partial failures where the overall operation succeeds (because 2/3 of nodes responded) but some nodes returned errors.
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { walrus } from '@mysten/walrus';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(
walrus({
storageNodeClientOptions: {
onError: (error) => console.log('Storage node error:', error),
},
}),
);
This does not change the behavior of the operation. Uploads still succeed if enough nodes respond. The onError callback provides visibility into which nodes are failing and why.
How to check consistency for critical data
By default, Walrus reads reconstruct the blob from slivers and verify the blob ID. For data where integrity is critical (for example, financial records or credentials), you can request a stricter consistency check that validates each individual sliver against the Merkle tree.
Using the CLI:
$ walrus read BLOB_ID --strict-consistency-check
Using the HTTP API:
$ curl "http://AGGREGATOR_URL:31415/BLOB_ID?consistency=strict"
Strict consistency checks are slower because they verify more data, but they provide stronger guarantees against sliver-level corruption.
Monitor errors in production
Enable verbose logging to capture detailed error information during uploads:
$ RUST_LOG=walrus=debug walrus store file.txt --epochs 5 2>&1 | tee upload.log
To identify recurring error patterns:
$ grep "Error" upload.log | sort | uniq -c
For production deployments using the TypeScript SDK, log errors from the onError callback and track metrics such as retry counts, timeout rates, and storage node error rates.
Debugging checklist
When you encounter an error, follow these steps in order:
- Enable debug logging with
RUST_LOG=walrus=debugand reproduce the error. - Check system status with
walrus infoandwalrus health --committee. - Verify your configuration is up to date and points to the correct network.
- Confirm your wallet has sufficient SUI and WAL balances.
- Verify you are using the latest CLI version with
walrus --version.
When you set RUST_LOG=info, the walrus CLI prints the path to the configuration file and Sui wallet it uses at startup. This helps confirm you are using the intended configuration.