Error Handling
Overview
Formbird's offline mode includes comprehensive error handling for network issues, sync failures, and data conflicts. The system distinguishes between fatal errors (permanent failures) and non-fatal errors (temporary issues that can be retried).
Error Categories
Fatal vs Non-Fatal Errors
| Type | Description | Behavior |
|---|---|---|
| Fatal | Permanent error that won't succeed on retry | Marked as error, creates error document |
| Non-Fatal | Temporary issue that may succeed on retry | Retried automatically |
HTTP Status Codes
| Status | Type | Description |
|---|---|---|
-1 |
Non-Fatal | Client offline / no connection |
400 |
Fatal | Bad request / invalid data |
401 |
Fatal | Authentication failure |
403 |
Fatal | Access denied |
500 |
Varies | Server error (may be temporary) |
502 |
Non-Fatal | Bad gateway (server starting) |
503 |
Non-Fatal | Service unavailable |
504 |
Non-Fatal | Gateway timeout |
Queue Error Handling
Queue Entry Status
Documents in the sync queue have an entryStatus:
const QUEUE_STATUS = {
UNPROCESSED: 0, // Waiting to sync
PROCESSED: 2, // Successfully synced
ERROR_FATAL: 9 // Fatal error, won't retry
};
Error Fields
Failed queue entries contain:
{
entryStatus: 9, // ERROR_FATAL
errorMessage: "403 - Read access denied",
errorCount: 1, // Number of sync attempts
errorCode: "FORBIDDEN"
}
Error Processing Logic
async processQueueEntry(entry) {
try {
await this.syncToServer(entry);
await this.markAsProcessed(entry.id);
} catch (error) {
if (this.isFatalError(error)) {
await this.markAsFatal(entry.id, error);
await this.createErrorDocument(entry, error);
} else {
// Leave unprocessed for retry
await this.incrementErrorCount(entry.id, error);
}
}
}
isFatalError(error) {
const fatalStatuses = [400, 401, 403];
return fatalStatuses.includes(error.status);
}
Error Documents
Creating Error Documents
When a fatal error occurs, the server creates an error document:
{
"documentId": "error-doc-uuid",
"systemHeader": {
"systemType": "error",
"templateId": "error-document-template-id",
"summaryName": "Sync Error - 2024-01-15"
},
"errorDocumentJSON": "{...truncated document JSON...}",
"errorMessage": "Read access denied on document abc-123",
"errorCode": "FORBIDDEN",
"errorCategory": "offline"
}
Configuration
Configure the error document template in serverConfiguration:
{
"serverConfiguration": {
"errorDocumentTemplate": "97c750af-f1d7-4e97-b6d9-13eb22dc3007"
}
}
If no template is configured, error documents are not saved and a warning is logged:
"Could not save error document because there is no error document configured"
Error Document Limits
errorDocumentJSONis truncated at 32KB to prevent storage issues- Error documents use the
offlineerror category - Error documents can be queried and displayed in a datatable
Gateway Error Handling
Offline Mode Behavior
When offline mode is enabled, gateway errors (HTTP 502, 504) are suppressed:
- User can continue using the app offline
- No "server starting" message displayed
- Data operations use IndexedDB
Sync Error Notifications
Error Count Display
Sync errors are displayed in the UI:
┌─────────────────────────────────────────┐
│ 👤 User Menu [3] │ ← Error count badge
│ ├── Enable Offline │
│ ├── Offline Status │
│ └── Error Count: 3 │ ← Errors waiting
└─────────────────────────────────────────┘
Error Messages
When sync fails, a notification shows:
Sync Error: Read access denied on document 14819c80-4696-11ec-87f2-51a31c959fb7
This alerts the implementor that: - Access keys may need to be added - The document has a permanent error - Manual intervention is required
Offline Status Service
class OfflineStatusService {
offlineErrorCount: number = 0;
errorOfflineChangesSyncDocs: string[] = [];
incrementErrorCount(documentId: string) {
this.offlineErrorCount++;
this.errorOfflineChangesSyncDocs.push(documentId);
}
}
Client State After Fatal Error
For Updates
If updating an existing document fails with a fatal error: - Current version is reset to server version - Local changes are discarded - Error document is created on server
For Inserts
If inserting a new document fails with a fatal error: - Document remains in IndexedDB - Error message displayed on form load: "There was a fatal error sending the document to the server." - User can modify and retry
Form Display
When loading a document with a fatal error:
async loadDocument(documentId) {
const queueEntry = await this.getQueueEntry(documentId);
if (queueEntry?.entryStatus === QUEUE_STATUS.ERROR_FATAL) {
// Display error message instead of form
return this.showError(
"There was a fatal error sending the document to the server."
);
}
// Normal document load
return this.dataService.getDocument(documentId);
}
Common Error Scenarios
Access Key Errors
Problem: Document syncs fail with "Read access denied"
Cause: Component or referenced documents don't have offline access key
Solution: 1. Add offline key to component documents 2. Add offline key to referenced documents 3. Verify user has offline access
Network Timeout Errors
Problem: Sync times out before completing
Cause: Slow network or large document
Solution: 1. Check network connection 2. Reduce document size 3. Increase timeout settings
Validation Errors
Problem: Document fails server-side validation
Cause: Invalid data format or missing required fields
Solution: 1. Review error message 2. Fix document data 3. Retry sync
Duplicate Key Errors
Problem: Insert fails with duplicate key error
Cause: Document already exists on server
Solution: 1. Check if document was previously synced 2. Use update instead of insert 3. Clear local queue entry
Error Recovery
Automatic Recovery
Non-fatal errors are automatically retried:
// Retry interval for non-fatal errors
const retryInterval = offlineConfig.cacheRetryInterval || 10000;
setInterval(() => {
this.processUnprocessedQueue();
}, retryInterval);
Manual Recovery
For fatal errors:
- View error - Check error document or notification
- Fix cause - Add access keys, fix data, etc.
- Retry - Make a new document save operation (i.e., save the document again)
Important: To retry after an error, the user must make a new document insert/operation. Simply waiting will not retry fatal errors - you must save the document again.
Clearing Failed Entries
The IndexedDB database name is unique to each user's document ID, not a fixed name like 'formbird'. To clear failed queue entries, you need to open the correct database:
// Sample code to open and clear failed entries
// First, create a Dexie instance with the correct database name
var db = new Dexie("YourDatabaseName");
db.version(1).stores({queue: "++id,entryStatus"});
db.open().then(function() {
return db.queue.where('entryStatus').equals(9).delete();
}).catch(function(err) {
console.error('Failed to clear entries: ' + (err.stack || err));
});
Note: You can find the correct database name by looking in DevTools → Application → IndexedDB.
Troubleshooting
Errors Not Displayed
Symptoms: - Sync fails silently - No error notification shown
Solutions:
1. Check console for error logs
2. Verify offline status component is loaded
3. Check offlineErrorCount in OfflineStatusService
Errors Keep Retrying
Symptoms: - Same error repeated continuously - Queue never completes
Solutions: 1. Verify error classification (fatal vs non-fatal) 2. Check if error should be marked fatal 3. Review error handling logic
Error Documents Not Created
Symptoms: - Fatal errors occur but no error documents saved
Solutions:
1. Verify errorDocumentTemplate is configured
2. Check server logs for document creation errors
3. Verify template has correct permissions
Gateway Error Loop
Symptoms: - "Server starting" message never resolves - App can't load
Solutions:
1. Check if Node server is running
2. Verify nginx proxy configuration
3. Increase maxAttempts setting
4. Check network connectivity
Best Practices
- Configure error template - Always set
errorDocumentTemplate - Add offline keys - Ensure all required documents have offline access
- Monitor error count - Watch for increasing errors
- Review error documents - Check periodically for patterns
- Test error scenarios - Verify error handling in development
- Handle gracefully - Don't expose technical errors to users
Related Documentation
- 101-Offline-Architecture.md - System overview
- 104-Offline-Syncing.md - Sync process
- 108-Offline-UI-Components.md - Status display
- 109-Configuration-Reference.md - Configuration options