Error Handling
This guide covers error types and handling strategies in the Privacy Boost TypeScript SDK.Error Types
All SDK errors extend fromPrivacyBoostError:
class PrivacyBoostError extends Error {
code: string; // Error code for programmatic handling
message: string; // Human-readable message
retryable: boolean; // Whether the operation can be retried
cause?: Error; // Original error if wrapped
isAuthError(): boolean; // Check if this is an auth-related error
isWalletError(): boolean; // Check if this is a wallet-related error
hasCode(): boolean; // Check if a specific error code is set
static from(error: unknown): PrivacyBoostError; // Wrap unknown errors
}
Error Categories
Configuration Errors
| Code | Description |
|---|---|
INVALID_CONFIG | Invalid SDK configuration |
try {
await PrivacyBoost.create({
serverUrl: 'invalid-url', // Will throw
// ...
});
} catch (error) {
if (error.code === 'INVALID_CONFIG') {
console.log('Please provide valid configuration');
}
}
Wallet Errors
| Code | Description |
|---|---|
TRANSACTION_REJECTED | User rejected the request |
WRONG_NETWORK | Wrong network |
try {
await sdk.auth.authenticate(adapter);
} catch (error) {
switch (error.code) {
case 'TRANSACTION_REJECTED':
showToast('Authentication cancelled');
break;
case 'WRONG_NETWORK':
showToast('Please switch to the correct network');
break;
default:
showToast('Authentication failed');
}
}
Authentication Errors
| Code | Description |
|---|---|
NOT_AUTHENTICATED | Not authenticated |
AUTH_NONCE_EXPIRED | Authentication nonce expired |
AUTH_INVALID_SIGNATURE | Invalid signature |
SESSION_EXPIRED | JWT token expired |
try {
await sdk.auth.authenticate(adapter);
} catch (error) {
if (error.code === 'AUTH_NONCE_EXPIRED') {
// Retry authentication
await sdk.auth.authenticate(adapter);
}
}
Operation Errors
| Code | Description |
|---|---|
TRANSFER_PROOF_FAILED | Transfer proof generation failed |
try {
await sdk.vault.shield(params);
} catch (error) {
switch (error.code) {
case 'TRANSACTION_REJECTED':
showError('Transaction cancelled');
break;
case 'TRANSFER_PROOF_FAILED':
showError('Proof generation failed');
break;
default:
showError('Deposit failed: ' + error.message);
}
}
Network Errors
| Code | Description |
|---|---|
TIMEOUT | Request timed out |
HTTP_ERROR | HTTP request failed |
RATE_LIMITED | Rate limited |
try {
await sdk.vault.getBalance(tokenAddress);
} catch (error) {
if (error.code === 'TIMEOUT') {
// Retry after delay
await delay(2000);
await sdk.vault.getBalance(tokenAddress);
}
}
Proof Errors
| Code | Description |
|---|---|
TRANSFER_PROOF_FAILED | Failed to generate transfer proof |
Error Handling Patterns
Basic Try-Catch
try {
await sdk.vault.shield(params);
} catch (error) {
if (error instanceof PrivacyBoostError) {
console.log('SDK Error:', error.code, error.message);
} else {
console.log('Unknown error:', error);
}
}
Error Handler Function
function handleError(error: unknown): string {
if (!(error instanceof PrivacyBoostError)) {
return 'An unexpected error occurred';
}
const messages: Record<string, string> = {
TRANSACTION_REJECTED: 'Request cancelled',
SESSION_EXPIRED: 'Session expired, please login again',
TIMEOUT: 'Request timed out, please try again',
WRONG_NETWORK: 'Please switch to the correct network',
};
return messages[error.code] || error.message;
}
// Usage
try {
await sdk.vault.transfer(params);
} catch (error) {
showToast(handleError(error));
}
Retry Logic
async function withRetry<T>(
operation: () => Promise<T>,
maxRetries = 3,
delay = 1000
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
// Don't retry user rejections
if (error.code === 'TRANSACTION_REJECTED') {
throw error;
}
// Retry retryable errors
if (error.retryable) {
await new Promise((r) => setTimeout(r, delay * attempt));
continue;
}
throw error;
}
}
throw lastError!;
}
// Usage
const balance = await withRetry(() => sdk.vault.getBalance(token));
Session Refresh
async function withSessionRefresh<T>(
operation: () => Promise<T>
): Promise<T> {
try {
return await operation();
} catch (error) {
if (error.code === 'SESSION_EXPIRED') {
await sdk.auth.authenticate(adapter);
return await operation();
}
throw error;
}
}
// Usage
const history = await withSessionRefresh(() =>
sdk.transactions.fetchHistory()
);
React Error Boundary
class PrivacyBoostErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ error: Error | null }
> {
state = { error: null };
static getDerivedStateFromError(error: Error) {
return { error };
}
render() {
if (this.state.error) {
return (
<div>
<h2>Something went wrong</h2>
<p>{this.state.error.message}</p>
<button onClick={() => this.setState({ error: null })}>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
React Hook for Error Handling
function useSDKOperation<T>(operation: () => Promise<T>) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<PrivacyBoostError | null>(null);
const [data, setData] = useState<T | null>(null);
const execute = useCallback(async () => {
setLoading(true);
setError(null);
try {
const result = await operation();
setData(result);
return result;
} catch (err) {
const sdkError = err instanceof PrivacyBoostError ? err : PrivacyBoostError.from(err);
setError(sdkError);
throw sdkError;
} finally {
setLoading(false);
}
}, [operation]);
return { execute, loading, error, data };
}
// Usage
function ShieldButton({ tokenAddress, amount }: { tokenAddress: Hex; amount: bigint }) {
const { execute, loading, error } = useSDKOperation(() =>
sdk.vault.shield({ tokenAddress, amount })
);
return (
<>
<button onClick={execute} disabled={loading}>
{loading ? 'Processing...' : 'Deposit'}
</button>
{error && <p className="error">{error.message}</p>}
</>
);
}
Logging Errors
function logError(error: unknown, context: string) {
if (error instanceof PrivacyBoostError) {
console.error(`[${context}] SDK Error:`, {
code: error.code,
message: error.message,
cause: error.cause,
});
} else {
console.error(`[${context}] Unknown error:`, error);
}
}
// Usage
try {
await sdk.vault.shield(params);
} catch (error) {
logError(error, 'deposit');
throw error;
}
Best Practices
1. Always Handle User Rejections Gracefully
if (error.code === 'TRANSACTION_REJECTED') {
// Don't show error, user intentionally cancelled
return;
}
2. Provide Actionable Error Messages
const actionableMessages: Record<string, string> = {
WRONG_NETWORK: 'Click here to switch networks',
SESSION_EXPIRED: 'Click to reconnect your wallet',
TRANSACTION_REJECTED: 'Transaction was cancelled',
};
3. Log Errors for Debugging
catch (error) {
// Log for developers
console.error('Operation failed:', error);
// Show user-friendly message
showToast(getUserMessage(error));
}
4. Use Error Codes, Not Messages
// BAD - Message might change
if (error.message.includes('rejected')) {
// ...
}
// GOOD - Code is stable
if (error.code === 'TRANSACTION_REJECTED') {
// ...
}