Troubleshooting
Common issues organized by symptom with step-by-step solutions
Extension Not Syncing
The extension appears connected but context data is not reaching your application.
Check authentication status. Open the extension popup and verify the user is signed in. An expired token causes silent sync failures. Re-authenticate if the status shows "Unauthenticated" or "Token expired".
Check privacy rules. The target domain may be in the user's restricted list. Periscope does not sync context from restricted domains. Review the extension's privacy settings or check your PrivacyFilterConfig for domain restrictions.
Check CORS configuration. If you are self-hosting the Periscope service, ensure your allowedOrigins configuration includes the origin of the extension. Browser security will block requests to origins not in the allow list.
Verify extension version. Outdated extension versions may use deprecated protocol fields. Check that the extension version matches or exceeds the minimum version required by your service deployment. The current extension version is available in the heartbeat payload's extensionVersion field.
WebSocket Disconnects
The connection drops repeatedly or fails to stay alive.
Heartbeat Timeout
The server closes connections that do not send a heartbeat within 60 seconds. Configure your client to send heartbeats every 25-30 seconds to provide a safety margin.
{
"heartbeatInterval": 25000
}
If you see connection_error with a message about heartbeat timeout, your heartbeat interval is too long or heartbeat messages are being dropped.
Reconnection Strategy
Use exponential backoff with jitter when reconnecting:
- Initial delay: 1 second
- Maximum delay: 30 seconds
- Backoff factor: 2x
- Jitter: Add random 0-500ms to each attempt
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);
const jitter = Math.random() * 500;
setTimeout(reconnect, delay + jitter);
Re-authentication After Reconnect
After a WebSocket reconnection, you must re-authenticate. The server does not preserve authentication state across connections. Send your Bearer token in the first message after the connection is established, or include it in the WebSocket URL query parameter.
Rate Limiting
You are receiving 429 Too Many Requests responses.
Reading Rate Limit Headers
Every response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Backoff Strategy
When you receive a 429 response:
- Read the
retryAfterfield from theSyncError(seconds to wait). - If
retryAfteris not present, use theX-RateLimit-Resetheader to calculate wait time. - Queue messages locally and flush after the wait period.
Use Batch Sync for High Volume
If your extension generates a high volume of events (e.g., frequent scroll or cursor.move events), use the batch sync endpoint (POST /context/batch-sync) instead of individual requests. Configure batchSize and batchTimeout in your SyncConfiguration to control batching behavior.
Privacy Violations
Sync requests are rejected with privacy-related errors.
Restricted Domains
Periscope automatically blocks context capture from:
latticeand127.0.0.10.0.0.0and[::1]- Any domain in the user's restricted domains list
If a domain is blocked, the extension silently skips context capture. No error is sent to the server.
PII Detection
The extension scans outgoing payloads for common PII patterns (email addresses, phone numbers, SSNs). If PII is detected in a non-sensitive field, the payload is rejected with a validation_error.
To resolve this:
- Check your
PrivacyFilterConfigfor PII detection rules. - Ensure sensitive form fields have
isSensitive: trueso values are redacted before transmission. - Use the
form.inputevent type's built-in redaction for password and credential fields.
Stale Context
Your application is receiving outdated context data.
Prefer WebSocket Over Polling
REST API polling introduces latency proportional to your poll interval. For real-time context, use the WebSocket API which pushes updates immediately.
Check Timestamp Freshness
Every SyncMessage includes a timestamp field. Compare this to the current time to detect stale data. Messages older than 30 seconds should be treated as potentially stale.
const messageAge = Date.now() - new Date(message.timestamp).getTime();
if (messageAge > 30_000) {
// Context may be stale; request a fresh sync
}
Extension Idle State
When the user is inactive, the extension stops sending context updates. It resumes when activity is detected. During idle periods, the last known context remains valid. Use the heartbeat message's activeTabId to determine if the extension is still active.
API Error Codes
Quick reference for HTTP status codes returned by the Periscope API.
| Status | Meaning | Action |
|---|---|---|
401 Unauthorized | Authentication token is missing, invalid, or expired | Re-authenticate and retry |
404 Not Found | The requested context ID does not exist | Verify the context ID; it may have been deleted |
207 Multi-Status | Batch request partially succeeded | Check individual results entries in the BatchSyncResponse |
429 Too Many Requests | Rate limit exceeded | Back off using retryAfter or X-RateLimit-Reset |
500 Internal Server Error | Server-side failure | Retry with exponential backoff; report if persistent |
SDK Connection Issues
Problems initializing or connecting with the Periscope SDK.
PeriscopeClient Constructor
Verify your constructor options:
const client = new PeriscopeClient({
apiKey: "your-api-key", // Required
wsUrl: "wss://periscope.uselovelace.com/ws", // WebSocket endpoint
apiUrl: "https://periscope.uselovelace.com/api/v1", // REST endpoint
heartbeatInterval: 25000, // Milliseconds (default: 30000)
reconnectAttempts: 5, // Max reconnection attempts (default: 3)
batchSize: 50, // Max messages per batch (default: 50)
timeout: 10000, // Request timeout in ms (default: 10000)
});
WebSocket URL Configuration
The WebSocket URL must use the wss:// scheme in production. Using ws:// (unencrypted) is only supported for local development.
If the connection fails immediately, check:
- The URL is correct and reachable from your network.
- Your firewall or proxy allows WebSocket connections.
- The path includes
/ws(not just the base domain).
Timeout Settings
The default request timeout is 10 seconds. For batch operations with large payloads, increase the timeout:
const client = new PeriscopeClient({
// ...
timeout: 30000, // 30 seconds for large batch operations
});
If you see timeout errors during initial connection, the server may be unreachable. Check the Lovelace Status Page for service availability.