Documentation Index
Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
Use this file to discover all available pages before exploring further.
Error Handling
This guide covers error handling patterns for the Privacy Boost Android SDK. For the cross-platform error code reference, see Error Handling.
SDKError
All SDK errors are thrown as SDKError sealed class instances:
sealed class SDKError : Exception() {
object NotConnected : SDKError() // Wallet not connected
object NotAuthenticated : SDKError() // Not logged in
object InvalidConfig : SDKError() // Invalid SDK configuration
data class NetworkError(override val message: String) : SDKError()
data class WalletError(override val message: String) : SDKError()
object SignatureRejected : SDKError() // User rejected signing
object InsufficientBalance : SDKError() // Not enough balance
object InvalidAddress : SDKError() // Invalid address format
object InvalidAmount : SDKError() // Invalid amount format
data class ApiError(val code: String, override val message: String, val retryable: Boolean) : SDKError()
data class RateLimited(val retryAfterMs: ULong) : SDKError()
data class DepositError(val code: String, override val message: String) : SDKError()
data class TransferError(val code: String, override val message: String) : SDKError()
data class InternalError(override val message: String) : SDKError()
// ... and more
}
Type-Safe Error Handling
Use Kotlin’s when expression for exhaustive error matching:
try {
val result = withContext(Dispatchers.IO) {
sdk.shield(tokenAddress = tokenAddress, amount = amount)
}
} catch (e: SDKError) {
when (e) {
is SDKError.SignatureRejected -> {
// User cancelled — do nothing
return
}
is SDKError.InsufficientBalance -> {
showToast("Not enough balance")
}
is SDKError.WalletError -> {
showToast("Wallet error: ${e.message}")
}
is SDKError.NetworkError -> {
showToast("Network error. Please try again.")
}
is SDKError.RateLimited -> {
val seconds = e.retryAfterMs / 1000u
showToast("Too many requests. Try again in $seconds seconds.")
}
is SDKError.ApiError -> {
if (e.retryable) {
// Retry the operation
} else {
showToast(e.message)
}
}
else -> {
showToast("An unexpected error occurred")
}
}
}
Retry with Exponential Backoff
For retryable errors, use exponential backoff:
suspend fun <T> withRetry(
maxRetries: Int = 3,
operation: suspend () -> T
): T {
for (attempt in 1..maxRetries) {
try {
return operation()
} catch (e: SDKError.NetworkError) {
if (attempt == maxRetries) throw e
delay(1000L * attempt)
} catch (e: SDKError.ApiError) {
if (!e.retryable || attempt == maxRetries) throw e
delay(1000L * attempt)
} catch (e: SDKError.RateLimited) {
if (attempt == maxRetries) throw e
delay(e.retryAfterMs.toLong())
}
}
throw SDKError.InternalError("Unreachable")
}
// Usage
val balance = withRetry {
withContext(Dispatchers.IO) {
sdk.getBalance(tokenAddress = token)
}
}
Auto Re-Authentication
Handle expired sessions by re-authenticating automatically:
suspend fun <T> withAuth(
wallet: WalletDelegate,
operation: suspend () -> T
): T {
return try {
operation()
} catch (e: SDKError.NotAuthenticated) {
// Session expired — re-authenticate
withContext(Dispatchers.IO) {
sdk.authenticate(wallet = wallet)
}
operation()
}
}
// Usage
val balance = withAuth(myWallet) {
withContext(Dispatchers.IO) {
sdk.getBalance(tokenAddress = token)
}
}
User-Friendly Error Messages
Map SDK errors to messages suitable for showing to users:
fun userMessage(error: SDKError): String = when (error) {
is SDKError.NotConnected -> "Please connect your wallet"
is SDKError.NotAuthenticated -> "Session expired. Please log in again."
is SDKError.SignatureRejected -> "Request cancelled"
is SDKError.InsufficientBalance -> "Not enough balance"
is SDKError.InvalidAddress -> "Invalid address"
is SDKError.InvalidAmount -> "Invalid amount"
is SDKError.NetworkError -> "Connection failed. Please check your internet and try again."
is SDKError.RateLimited -> "Too many requests. Please wait a moment."
is SDKError.WalletError -> "Wallet error: ${error.message}"
else -> "Something went wrong. Please try again."
}
Categorizing Errors
Group errors by whether user action can resolve them:
enum class ErrorCategory {
USER_ACTION, // User can fix (e.g., connect wallet, add funds)
RETRYABLE, // Transient, retry may work
FATAL // Requires developer attention
}
fun categorize(error: SDKError): ErrorCategory = when (error) {
is SDKError.NotConnected,
is SDKError.NotAuthenticated,
is SDKError.SignatureRejected,
is SDKError.InsufficientBalance,
is SDKError.InvalidAddress,
is SDKError.InvalidAmount -> ErrorCategory.USER_ACTION
is SDKError.NetworkError,
is SDKError.RateLimited -> ErrorCategory.RETRYABLE
is SDKError.ApiError -> if (error.retryable) ErrorCategory.RETRYABLE else ErrorCategory.FATAL
else -> ErrorCategory.FATAL
}
ViewModel Error Handling Pattern
class VaultViewModel(
private val sdk: PrivacyBoost,
private val wallet: WalletDelegate
) : ViewModel() {
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error.asStateFlow()
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
fun deposit(tokenAddress: String, amount: String) {
viewModelScope.launch {
_isLoading.value = true
_error.value = null
try {
val result = withRetry {
withContext(Dispatchers.IO) {
sdk.shield(tokenAddress = tokenAddress, amount = amount)
}
}
println("Deposit successful: ${result.txHash}")
} catch (e: SDKError.SignatureRejected) {
// User cancelled — no error to show
} catch (e: SDKError) {
_error.value = userMessage(e)
}
_isLoading.value = false
}
}
}
Best Practices
1. Always Handle SignatureRejected Silently
When a user cancels a wallet signature, don’t show an error:
try {
val result = withContext(Dispatchers.IO) {
sdk.shield(tokenAddress = token, amount = amount)
}
} catch (e: SDKError.SignatureRejected) {
return // User cancelled, nothing to report
} catch (e: SDKError) {
showToast(userMessage(e))
}
2. Log Errors for Debugging
try {
val result = withContext(Dispatchers.IO) {
sdk.shield(tokenAddress = token, amount = amount)
}
} catch (e: SDKError) {
// Log full error for debugging
Log.e("PrivacyBoost", "Deposit failed", e)
// Show user-friendly message
showToast(userMessage(e))
}
3. Use Error Categories for UI Decisions
try {
// some operation
} catch (e: SDKError) {
when (categorize(e)) {
ErrorCategory.USER_ACTION -> showToast(userMessage(e))
ErrorCategory.RETRYABLE -> showRetryButton()
ErrorCategory.FATAL -> showContactSupport()
}
}
Next Steps