Error Handling
This page covers how to handle Walrus errors programmatically in your applications. For a reference of specific error messages and their solutions, see Troubleshooting Common Errors.
Error categories
Not all errors are equal. Some are safe to retry, some require you to check on-chain state (an operation might have succeeded on-chain 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 on-chain 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 on-chain state before retrying. A timed-out transaction might have succeeded. Retrying without checking can result in duplicate blob registrations and wasted funds.
Implement retry logic 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.
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(`Retry attempt ${attempt}/${maxRetries} after ${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.
Verify on-chain state after timeouts
When an upload or transaction times out, the operation might have succeeded on-chain 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 it was not found, the transaction might have been dropped from the mempool and 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.
Use strict consistency checks 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.