Skip to main content

Error Reference

Complete reference for error types and error handling in Unforgettable SDK.

Error Types

NotFoundError

Data transfer not found or not yet available.

When thrown:

  • User hasn't completed recovery process
  • Transfer ID doesn't exist
  • Data expired (past TTL)
import { NotFoundError } from '@rarimo/unforgettable-sdk'

try {
const key = await sdk.getRecoveredKey()
} catch (error) {
if (error instanceof NotFoundError) {
console.log('Data not ready yet, continue polling')
}
}

Recommended Action: Continue polling


NetworkError

Network connectivity or HTTP request failed.

When thrown:

  • No internet connection
  • API server unreachable
  • Request timeout
  • HTTP error codes (500, 503, etc.)
try {
const key = await sdk.getRecoveredKey()
} catch (error) {
if (error.name === 'NetworkError') {
console.error('Network issue:', error.message)
// Show retry button to user
}
}

Recommended Action: Retry with exponential backoff


CryptoError

Cryptographic operation failed.

When thrown:

  • Key generation failed
  • Encryption failed
  • Decryption failed
  • Invalid encryption key
  • Malformed encrypted data
try {
const key = await sdk.getRecoveredKey()
} catch (error) {
if (error.name === 'CryptoError') {
console.error('Decryption failed:', error.message)
// Data may be corrupted, abort
}
}

Recommended Action: Abort and restart recovery process


InvalidResponse

API returned unexpected or malformed response.

When thrown:

  • Response doesn't match expected schema
  • Missing required fields
  • Invalid JSON
try {
const key = await sdk.getRecoveredKey()
} catch (error) {
if (error.name === 'InvalidResponse') {
console.error('API returned invalid data')
}
}

Recommended Action: Retry or report error


DecodingError

Failed to decode/parse data.

When thrown:

  • JSON parsing failed
  • Base64 decoding failed
  • Invalid data format
try {
const key = await sdk.getRecoveredKey()
} catch (error) {
if (error.name === 'DecodingError') {
console.error('Failed to parse response')
}
}

Recommended Action: Retry or abort

Error Handling Patterns

Basic Try-Catch

async function recoverKey(sdk: UnforgettableSdk): Promise<string | null> {
try {
return await sdk.getRecoveredKey()
} catch (error) {
console.error('Recovery failed:', error)
return null
}
}

Specific Error Handling

async function recoverKeyWithRetry(sdk: UnforgettableSdk): Promise<string> {
try {
return await sdk.getRecoveredKey()
} catch (error) {
if (error instanceof NotFoundError) {
// Not ready, caller should retry
throw error
} else if (error.name === 'NetworkError') {
// Network issue, retry after delay
await sleep(5000)
return recoverKeyWithRetry(sdk)
} else if (error.name === 'CryptoError') {
// Crypto failure, abort
throw new Error('Decryption failed, please restart recovery')
} else {
// Unknown error
throw error
}
}
}

Polling with Error Handling

async function pollForKey(
sdk: UnforgettableSdk,
maxAttempts: number = 60
): Promise<string> {
let attempts = 0
let lastError: Error | null = null

while (attempts < maxAttempts) {
try {
return await sdk.getRecoveredKey()
} catch (error) {
lastError = error as Error

if (error instanceof NotFoundError) {
// Expected during polling
attempts++
await sleep(3000)
} else if (error.name === 'NetworkError') {
// Network issue, longer wait
attempts++
await sleep(10000)
} else {
// Other errors, abort
throw error
}
}
}

throw new Error(`Polling timeout after ${attempts} attempts. Last error: ${lastError?.message}`)
}

User-Friendly Error Messages

function getErrorMessage(error: Error): string {
if (error instanceof NotFoundError) {
return 'Please complete the recovery process on your mobile device'
} else if (error.name === 'NetworkError') {
return 'Network connection issue. Please check your internet and try again'
} else if (error.name === 'CryptoError') {
return 'Recovery data is invalid. Please restart the recovery process'
} else if (error.name === 'InvalidResponse') {
return 'Server error. Please try again later'
} else {
return 'An unexpected error occurred. Please try again'
}
}

// Usage
try {
const key = await sdk.getRecoveredKey()
} catch (error) {
alert(getErrorMessage(error as Error))
}

Error Logging

Development

async function recoverWithLogging(sdk: UnforgettableSdk): Promise<string> {
try {
console.log('[Recovery] Starting...')
const key = await sdk.getRecoveredKey()
console.log('[Recovery] Success!')
return key
} catch (error) {
console.error('[Recovery] Error:', {
type: error.name,
message: error.message,
stack: error.stack,
})
throw error
}
}

Production

async function recoverWithTracking(sdk: UnforgettableSdk): Promise<string> {
try {
const key = await sdk.getRecoveredKey()
analytics.track('recovery_success')
return key
} catch (error) {
// Log error without exposing sensitive data
errorTracking.captureException(error, {
tags: {
component: 'unforgettable-sdk',
method: 'getRecoveredKey',
},
extra: {
errorType: error.name,
// DO NOT log private keys or sensitive data
},
})
throw error
}
}

Platform-Specific Errors

Android (Kotlin)

sealed class UnforgettableSDKError : Exception() {
data class NetworkError(override val cause: Throwable) : UnforgettableSDKError()
object InvalidResponse : UnforgettableSDKError()
object NotFound : UnforgettableSDKError()
data class DecodingError(override val cause: Throwable) : UnforgettableSDKError()
data class CryptoError(override val cause: Throwable) : UnforgettableSDKError()
}

sealed class CryptoError : Exception() {
object KeyGenerationFailed : CryptoError()
object EncryptionFailed : CryptoError()
object DecryptionFailed : CryptoError()
object InvalidPublicKey : CryptoError()
object EncodingFailed : CryptoError()
object DecodingFailed : CryptoError()
}

iOS (Swift)

public enum UnforgettableSDKError: Error {
case networkError(Error)
case invalidResponse
case notFound
case decodingError(Error)
case cryptoError(Error)
}

public enum CryptoError: Error {
case keyGenerationFailed
case encryptionFailed
case decryptionFailed
case invalidPublicKey
case encodingFailed
case decodingFailed
}

Best Practices

1. Always Handle Errors

Bad:

const key = await sdk.getRecoveredKey() // No error handling!

Good:

try {
const key = await sdk.getRecoveredKey()
} catch (error) {
handleError(error)
}

2. Distinguish Between Error Types

Bad:

try {
const key = await sdk.getRecoveredKey()
} catch (error) {
console.error('Error!') // Generic handling
}

Good:

try {
const key = await sdk.getRecoveredKey()
} catch (error) {
if (error instanceof NotFoundError) {
// Continue polling
} else if (error.name === 'NetworkError') {
// Retry
} else {
// Abort
}
}

3. Provide User Feedback

Bad:

try {
const key = await sdk.getRecoveredKey()
} catch (error) {
console.error(error) // User sees nothing
}

Good:

try {
const key = await sdk.getRecoveredKey()
} catch (error) {
showErrorMessage(getErrorMessage(error))
enableRetryButton()
}

4. Don't Log Sensitive Data

Bad:

try {
const key = await sdk.getRecoveredKey()
console.log('Recovered key:', key) // Private key exposed!
} catch (error) {
console.error(error)
}

Good:

try {
const key = await sdk.getRecoveredKey()
console.log('Recovery successful')
// Use key without logging it
} catch (error) {
console.error('Recovery failed:', error.name)
}

Next Steps