Skip to content

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

  • errorDocumentJSON is truncated at 32KB to prevent storage issues
  • Error documents use the offline error 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:

  1. View error - Check error document or notification
  2. Fix cause - Add access keys, fix data, etc.
  3. 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

  1. Configure error template - Always set errorDocumentTemplate
  2. Add offline keys - Ensure all required documents have offline access
  3. Monitor error count - Watch for increasing errors
  4. Review error documents - Check periodically for patterns
  5. Test error scenarios - Verify error handling in development
  6. Handle gracefully - Don't expose technical errors to users