Skip to main content

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.

json
{
  "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
typescript
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:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

Backoff Strategy

When you receive a 429 response:

  1. Read the retryAfter field from the SyncError (seconds to wait).
  2. If retryAfter is not present, use the X-RateLimit-Reset header to calculate wait time.
  3. 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:

  • lattice and 127.0.0.1
  • 0.0.0.0 and [::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:

  1. Check your PrivacyFilterConfig for PII detection rules.
  2. Ensure sensitive form fields have isSensitive: true so values are redacted before transmission.
  3. Use the form.input event 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.

typescript
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.

StatusMeaningAction
401 UnauthorizedAuthentication token is missing, invalid, or expiredRe-authenticate and retry
404 Not FoundThe requested context ID does not existVerify the context ID; it may have been deleted
207 Multi-StatusBatch request partially succeededCheck individual results entries in the BatchSyncResponse
429 Too Many RequestsRate limit exceededBack off using retryAfter or X-RateLimit-Reset
500 Internal Server ErrorServer-side failureRetry with exponential backoff; report if persistent

SDK Connection Issues

Problems initializing or connecting with the Periscope SDK.

PeriscopeClient Constructor

Verify your constructor options:

typescript
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:

  1. The URL is correct and reachable from your network.
  2. Your firewall or proxy allows WebSocket connections.
  3. 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:

typescript
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.