This document describes the error handling patterns used throughout react-native-sync-vault.
- Fail Gracefully: Never crash the app due to queue/sync errors
- Log Clearly: Provide detailed error information for debugging
- Recover Automatically: Retry transient errors automatically
- Inform Users: Surface errors appropriately in the UI
- Type:
network - Retriable: Yes (with exponential backoff)
- Examples: Connection timeout, DNS failure, no internet
- Handling: Automatically retried up to
maxRetries
// Network errors are automatically retried
await queue.push({
method: 'POST',
url: '/api/users',
data: { name: 'John' },
maxRetries: 3, // Will retry up to 3 times
});- Type:
server - Retriable: Yes
- Examples: 500 Internal Server Error, 503 Service Unavailable
- Handling: Retried with exponential backoff
- Type:
validationorunknown - Retriable: Conditional
- Examples: 400 Bad Request, 401 Unauthorized, 404 Not Found
- Handling:
- Validation errors (400): Not retried (permanent failure)
- Auth errors (401, 403): Not retried (requires user action)
- Not found (404): Not retried (permanent failure)
- Other 4xx: Evaluated case-by-case
- Type:
conflict - Retriable: Yes (with conflict resolution)
- Examples: 409 Conflict (version mismatch, concurrent updates)
- Handling: Resolved using configured conflict resolution strategy
await queue.push({
method: 'PUT',
url: '/api/users/1',
data: { name: 'Updated' },
conflictResolution: 'client_wins', // or 'server_wins', 'manual_merge', 'custom'
});- Type:
timeout - Retriable: Yes
- Examples: Request timeout, connection timeout
- Handling: Retried with exponential backoff
All critical operations are wrapped in try-catch:
try {
const requestId = await queue.push(options);
// Success
} catch (error) {
// Error is logged and re-thrown for caller to handle
console.error('Failed to queue request:', error);
throw error;
}When native modules are unavailable:
if (!NativeModule) {
console.warn('Native module not available');
// Return safe default or throw descriptive error
throw new Error('Native module required');
}Errors are logged with context:
console.error('[QueueManager] Failed to queue request:', {
error: error.message,
request: { method, url },
stack: error.stack,
});Failed requests are automatically retried:
- Exponential Backoff: Delay increases with each attempt
- Max Retries: Configurable per request (default: 3)
- Retry Conditions: Only retriable errors are retried
// Custom retry configuration
await queue.push({
method: 'POST',
url: '/api/users',
data: { name: 'John' },
maxRetries: 5, // Retry up to 5 times
});Failed requests can be manually retried:
const { failedRequests, retry } = useFailedRequests();
// Retry a specific failed request
await retry(failedRequests[0].requestId);Track error states in your UI:
const { status } = useQueueStatus();
if (status?.totalFailed > 0) {
// Show error indicator
console.log('Failed requests:', status.totalFailed);
}const handleSubmit = async () => {
try {
await queue.push({
method: 'POST',
url: '/api/users',
data: formData,
});
showSuccess('Request queued successfully');
} catch (error) {
showError('Failed to queue request. Please try again.');
console.error('Queue error:', error);
}
};const { failedRequests } = useFailedRequests();
useEffect(() => {
if (failedRequests.length > 0) {
// Notify user or log for monitoring
console.warn('Failed requests detected:', failedRequests);
}
}, [failedRequests]);const { status } = useQueueStatus();
// Show sync status
if (status?.syncInProgress) {
return <Text>Syncing...</Text>;
}
if (status?.totalFailed > 0) {
return <Text>Some requests failed. Check failed requests.</Text>;
}const { status } = useQueueStatus();
if (status?.networkStatus === 'offline') {
// Show offline indicator
return <Text>You're offline. Requests will sync when online.</Text>;
}- Queue Full: Database storage limit reached
- Invalid Request: Malformed request data
- Native Module Error: Native module unavailable or failed
- Network Unavailable: Cannot sync (expected when offline)
- Server Error: Backend returned error
- Conflict: Data conflict detected
- Timeout: Request timed out
- Permission Denied: Network permission not granted (Android)
- Native Module Unavailable: Network monitoring disabled
The library includes extensive debug logging:
// Debug logs are automatically enabled in development
// Look for [DEBUG] prefixed logs in consoleconst { failedRequests } = useFailedRequests();
failedRequests.forEach(request => {
console.log('Failed request:', {
id: request.requestId,
error: request.errorMessage,
type: request.failureType,
willRetry: request.willRetry,
nextRetryAt: request.nextRetryAt,
});
});import { DebugScreen } from 'react-native-sync-vault';
// In development
{__DEV__ && <DebugScreen />}Error: "Native module not available"
Solution:
- Rebuild the app:
cd ios && pod install && cd .. && npx react-native run-ios - Check autolinking is enabled
- Verify native modules are properly linked
Error: Network status always "unknown"
Solution: Add permission to AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />Error: Requests stay in pending state
Solution:
- Check network status:
status.networkStatus - Verify sync is running:
status.syncInProgress - Manually trigger sync:
await queue.sync()
Error: "Failed to queue request"
Solution:
- Check database is initialized
- Verify native module is available
- Check request data is valid JSON
- Review error logs for details
When reporting errors:
- Include Error Message: Full error text
- Provide Context: What operation failed
- Share Logs: Relevant console logs
- Describe Environment: React Native version, platform, device
- Reproduction Steps: How to reproduce the error
- ✅ Network errors: Automatically retried
- ✅ Server errors: Automatically retried
- ✅ Validation errors: Not retried (permanent)
- ✅ Conflicts: Resolved using strategy
- ✅ Timeouts: Automatically retried
- ✅ All errors: Logged with context
- ✅ Failed requests: Tracked and retriable
For more information, see:
- API Reference
- Troubleshooting Guide (if created)
- GitHub Issues