Troubleshooting
This guide covers common issues you may encounter when using TopGun, organized by category.
CRDT Merge Edge Cases
Data reverted after reconnect
Symptom: After going offline and reconnecting, some of your local changes appear to revert to an older state.
Cause: TopGun uses Hybrid Logical Clocks (HLC) to order writes. Each write carries a timestamp of {millis, counter, nodeId}. When two clients write to the same field, the write with the higher HLC timestamp wins (Last-Write-Wins semantics). If your local clock is behind the server or another client, your writes may have lower timestamps.
Solutions:
- Ensure system clocks are reasonably synchronized (NTP). TopGun tolerates moderate clock skew, but large drift (minutes+) can cause unexpected ordering
- Check that
nodeIdis unique per client instance — duplicate node IDs cause HLC collisions - Remember that LWW is per-field, not per-record. Only the conflicting fields revert; other fields retain their values
Concurrent edits lost
Symptom: Two users edit different items in a collection simultaneously, but one user’s additions disappear after sync.
Cause: If you are using LWWMap for a collection where items are added and removed concurrently, LWW semantics may cause one write to overwrite another when they modify the same key.
Solutions:
- Use
ORMap(Observed-Remove Map) for collections where concurrent additions and removals are expected. ORMap uses unique tags to track each add operation, allowing concurrent adds to coexist - Reserve
LWWMapfor single-document fields where last-write-wins is the desired behavior - See the Data Structures guide for details on when to use each CRDT type
Stale data after merge
Symptom: After a sync cycle completes, some records still show old values even though newer writes exist on the server.
Cause: MerkleTree-based delta sync compares hash digests to determine which records need syncing. If the sync was interrupted or only partially completed, some records may not have been transferred yet.
Solutions:
- Wait for the sync cycle to fully complete. Check the
SyncEngineconnection state — it should transition toconnectedand thensynced - For large datasets, sync happens in batches. Initial sync after a long offline period may take several seconds
- If the issue persists, the client’s local MerkleTree may be corrupted. Clearing IndexedDB storage and re-syncing will rebuild it from the server
WebSocket Connection Issues
Connection drops repeatedly
Symptom: The client connects briefly then disconnects in a loop, or never establishes a stable connection.
Solutions:
- Verify the server address format: use
ws://for local development andwss://for production (TLS) - Check that the server is actually running and listening on the expected port
- If behind a reverse proxy (nginx, Cloudflare), ensure WebSocket upgrade headers are forwarded. Common nginx configuration:
proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; - Increase proxy timeout settings. Many proxies close idle WebSocket connections after 60 seconds. TopGun sends heartbeats, but aggressive proxy timeouts can still cause drops
Sync not working behind corporate proxy
Symptom: The application works on home WiFi but fails to sync in a corporate environment.
Cause: Many corporate proxies block WebSocket upgrade requests (HTTP 101 Switching Protocols) or filter non-HTTP traffic.
Solutions:
- Use
wss://(WebSocket over TLS) which is harder for proxies to inspect and block - If the proxy terminates TLS, you may need to configure the proxy to allow WebSocket upgrades
- Check with your network administrator whether WebSocket traffic is explicitly blocked
- TopGun clients automatically queue writes when offline and sync when connectivity is restored, so the application remains usable even if sync is intermittently blocked
Authentication failed
Symptom: Connection is rejected with an authentication error, or the server returns a 401/403 status.
Cause: TopGun uses JWT tokens for authentication. The token must contain a sub (subject) claim per RFC 7519.
Solutions:
- Verify your JWT token has not expired. Check the
expclaim - Ensure the token contains a
subclaim with the user ID. TopGun uses only the standardsubclaim — there is no customuserIdfield - Verify the token is signed with the same secret or key pair configured on the server
- Check that the token is passed correctly in the connection configuration:
const client = new TopGunClient({ serverUrl: 'wss://your-server.com', auth: { token: 'your-jwt-token' } });
IndexedDB Persistence
Data gone after browser update
Symptom: After a browser update or clearing browser data, locally stored records are gone.
Cause: IndexedDB storage can be evicted by the browser under storage pressure, especially if the site is not installed as a PWA or if the user has not interacted with it recently.
Solutions:
- Request persistent storage to reduce eviction risk:
if (navigator.storage && navigator.storage.persist) { const granted = await navigator.storage.persist(); console.log('Persistent storage granted:', granted); } - Inform users that critical data is synced to the server. Local storage is a cache for offline access, not the sole source of truth
- Consider using a Service Worker to register the app as a PWA, which gives it higher storage priority
Slow startup with large dataset
Symptom: The application takes several seconds to become interactive when there is a lot of data in IndexedDB.
Cause: On startup, the IDBAdapter loads stored records into memory to hydrate the local CRDT state.
Solutions:
- Use selective sync to limit which data is loaded at startup. Only subscribe to the maps and queries your initial view needs
- Paginate large collections using query limits rather than loading everything at once
- Consider using
fieldsprojection in queries to load only the fields you need for list views, then fetch full records on demand
Server Startup
Port already in use
Symptom: The server fails to start with “Address already in use” error.
Solutions:
- Check if another TopGun instance or other service is using the port:
lsof -i :8080 - Kill the existing process or choose a different port:
PORT=8081 cargo run --bin test-server --release - If running in development, ensure previous server processes are terminated before restarting
PostgreSQL connection failed
Symptom: The server starts but fails to connect to PostgreSQL, logging connection errors.
Solutions:
- Verify the connection string format:
postgresql://user:password@host:port/database - Check that PostgreSQL is running and accepting connections on the configured host and port
- If using SSL, ensure the
sslmodeparameter matches your PostgreSQL configuration (e.g.,sslmode=requireorsslmode=prefer) - Verify the database exists and the user has appropriate permissions
- Check PostgreSQL’s
pg_hba.conffor client authentication rules
Cluster nodes not discovering each other
Symptom: Multiple server nodes are running but not forming a cluster. Each node operates independently.
Solutions:
- Verify that all nodes are configured with the same cluster name/identifier
- Ensure the cluster port (separate from the client-facing WebSocket port) is accessible between nodes. Check firewall rules
- Verify that nodes can resolve each other’s hostnames or IP addresses
- Check that the seed node addresses are correctly configured in each node’s cluster configuration
- Review server logs with
RUST_LOG=topgun_server=debugto see cluster protocol messages and identify where discovery fails