Skip to content

Home > Documentation > Troubleshooting

Troubleshooting Guide

Common issues, debugging strategies, and solutions for ParleyJS.

Table of Contents

  1. Connection Issues
  2. Message Not Received
  3. Origin Validation Errors
  4. Timeout Errors
  5. Type Errors
  6. Performance Issues
  7. Debugging Strategies
  8. Common Mistakes

Connection Issues

Problem: Unable to connect to iframe/window

Symptoms:

  • connect() never resolves
  • Connection timeout errors
  • No handshake received

Common Causes:

  1. Window not ready yet
javascript
// Wrong - iframe might not be loaded
const parley = Parley.create({ allowedOrigins: ['https://child.com'] });
await parley.connect(iframe.contentWindow, 'child');

// Correct - wait for iframe to load
const iframe = document.querySelector('#child-iframe');
await new Promise((resolve) => {
    if (iframe.contentDocument?.readyState === 'complete') {
        resolve();
    } else {
        iframe.addEventListener('load', resolve, { once: true });
    }
});

await parley.connect(iframe.contentWindow, 'child');
  1. Both sides not calling connect()
javascript
// Both parent and child must call connect()

// Parent
await parley.connect(iframe.contentWindow, 'child');

// Child (in iframe)
await parley.connect(window.parent, 'parent');
  1. Wrong origin configured
javascript
// Check that origins match exactly
// Parent expects: https://child.example.com
// Child is actually: http://child.example.com (wrong protocol!)

Solution Checklist:

  • [ ] Both windows call connect()
  • [ ] Wait for iframe/window to fully load before connecting
  • [ ] Origins are configured correctly on both sides
  • [ ] No CORS or security policy blocking communication

Message Not Received

Problem: Handler not called when message is sent

Symptoms:

  • send() completes without error
  • No handler is invoked
  • Silent failure

Common Causes:

  1. Handler registered after message sent
javascript
// Wrong - race condition
await parley.send('hello', {}, { targetId: 'child' });
parley.on('hello', (payload) => console.log(payload)); // Registered too late!

// Correct - register handler first
parley.on('hello', (payload) => console.log(payload));
await parley.connect(iframe.contentWindow, 'child');
await parley.send('hello', {}, { targetId: 'child' });
  1. Wrong message type
javascript
// Sender
await parley.send('user-login', data, { targetId: 'child' });

// Receiver - typo!
parley.on('userLogin', (data) => {}); // Wrong type!

// Should be:
parley.on('user-login', (data) => {}); // Matches sender
  1. Origin mismatch (silently rejected)
javascript
// Parent configured origin
const parley = Parley.create({
    allowedOrigins: ['https://child.example.com'],
});

// But child is running on http://child.example.com
// Messages silently rejected due to origin mismatch

Solution Checklist:

  • [ ] Handler registered before messages are sent
  • [ ] Message type strings match exactly (case-sensitive)
  • [ ] Origins configured correctly on both sides
  • [ ] Both sides have called connect()

Debugging:

javascript
// Enable debug mode to see all messages
const parley = Parley.create({
    allowedOrigins: ['https://child.com'],
    debug: true, // Logs all messages to console
});

// Listen for system events
parley.onSystem(SYSTEM_EVENTS.MESSAGE_RECEIVED, (event) => {
    console.log('Received:', event);
});

Origin Validation Errors

Problem: SecurityError - Origin validation failed

Symptoms:

  • SecurityError: Origin mismatch errors
  • Messages silently dropped
  • Connection fails

Common Causes:

  1. Port mismatch
javascript
// Parent expects
allowedOrigins: ['https://child.com:443'];

// But browser normalizes to
('https://child.com'); // Port 443 is default for HTTPS

// Solution: Don't specify default ports
allowedOrigins: ['https://child.com']; // Correct
  1. Protocol mismatch
javascript
// Wrong
allowedOrigins: ['http://child.com']; // HTTP

// Child is actually running on
('https://child.com'); // HTTPS

// Solution: Match protocol exactly
allowedOrigins: ['https://child.com'];
  1. Subdomain mismatch
javascript
// Parent expects
allowedOrigins: ['https://child.example.com'];

// But child is on
('https://www.child.example.com'); // Different subdomain!

// Solution: Add all subdomains
allowedOrigins: ['https://child.example.com', 'https://www.child.example.com'];
  1. Development vs Production
javascript
// Development
allowedOrigins: ['http://localhost:3000'];

// Production
allowedOrigins: ['https://app.example.com'];

// Solution: Use environment variables
allowedOrigins: [process.env.ALLOWED_ORIGIN];

Solution:

javascript
// Log the actual origin to debug
window.addEventListener('message', (event) => {
    console.log('Message from origin:', event.origin);
});

// Then configure ParleyJS with the exact origin
const parley = Parley.create({
    allowedOrigins: ['https://exact-origin.com'], // Use exact origin from log
});

Timeout Errors

Problem: TimeoutError - Request timed out

Symptoms:

  • TimeoutError thrown
  • Requests never resolve
  • Long wait before error

Common Causes:

  1. Handler never responds
javascript
// Wrong - handler doesn't call respond()
parley.on('get-data', (payload, respond) => {
    const data = fetchData(payload.id);
    // Forgot to call respond()!
});

// Correct - always respond
parley.on('get-data', async (payload, respond) => {
    const data = await fetchData(payload.id);
    respond({ data }); // Always respond!
});
  1. Handler throws error
javascript
// If handler throws, no response is sent
parley.on('get-data', async (payload, respond) => {
    throw new Error('Something went wrong'); // Causes timeout!
});

// Correct - catch errors and respond
parley.on('get-data', async (payload, respond) => {
    try {
        const data = await fetchData(payload.id);
        respond({ success: true, data });
    } catch (error) {
        respond({ success: false, error: error.message });
    }
});
  1. Timeout too short
javascript
// Wrong - slow operation with short timeout
await parley.send(
    'slow-query',
    {},
    {
        targetId: 'child',
        timeout: 1000, // Only 1 second!
    }
);

// Correct - increase timeout for slow operations
await parley.send(
    'slow-query',
    {},
    {
        targetId: 'child',
        timeout: 30000, // 30 seconds
    }
);

Solution Checklist:

  • [ ] Handler always calls respond()
  • [ ] Handler catches errors and responds with error
  • [ ] Timeout is long enough for operation
  • [ ] Handler is actually registered

Type Errors

Problem: TypeScript type errors or runtime type mismatches

Common Causes:

  1. Missing generic types
typescript
// Wrong - no type safety
const response = await parley.send('get-user', { id: 123 });
console.log(response.name); // No type checking!

// Correct - specify types
interface GetUserRequest {
    id: number;
}

interface GetUserResponse {
    name: string;
    email: string;
}

const response = await parley.send<GetUserRequest, GetUserResponse>(
    'get-user',
    { id: 123 },
    { targetId: 'child' }
);
console.log(response.name); // Type-safe!
  1. Handler type mismatch
typescript
// Wrong - handler expects different type
parley.on<{ userId: string }>('get-user', (payload) => {
    const id: number = payload.userId; // Type error!
});

// Correct - handler matches request type
parley.on<GetUserRequest>('get-user', (payload) => {
    const id: number = payload.id; // Type-safe!
});

Solution: Always specify generic types for type safety.


Performance Issues

Problem: Slow message delivery or high latency

Common Causes:

  1. Large payloads
javascript
// Wrong - sending huge object
await parley.send('update', {
    data: hugeArray, // 10MB of data!
    timestamp: Date.now(),
});

// Correct - send smaller chunks or reference
await parley.send('update', {
    dataId: 'ref-123', // Just send reference
    timestamp: Date.now(),
});
  1. Too many messages
javascript
// Wrong - sending in tight loop
for (let i = 0; i < 1000; i++) {
    await parley.send('update', { index: i }); // Too many!
}

// Correct - batch messages
const batch = [];
for (let i = 0; i < 1000; i++) {
    batch.push({ index: i });
}
await parley.send('batch-update', { items: batch });
  1. Synchronous blocking operations
javascript
// Wrong - blocking operation in handler
parley.on('process', (payload, respond) => {
  const result = expensiveSync Operation(); // Blocks!
  respond({ result });
});

// Correct - use async operations
parley.on('process', async (payload, respond) => {
  const result = await expensiveAsyncOperation();
  respond({ result });
});

Solutions:

  • Keep payloads small (< 1MB)
  • Batch messages when sending multiple items
  • Use async/await for slow operations
  • Consider debouncing frequent updates

For more details, see Performance Best Practices. For batching messages in request-response pattern, see Batch Request-Response.


Debugging Strategies

Enable Debug Mode

javascript
const parley = Parley.create({
    allowedOrigins: ['https://child.com'],
    debug: true, // Enables detailed logging
});

Monitor System Events

javascript
import { SYSTEM_EVENTS } from 'parley-js';

// Connection events
parley.onSystem(SYSTEM_EVENTS.CONNECTED, (event) => {
    console.log('Connected to:', event.targetId);
});

parley.onSystem(SYSTEM_EVENTS.DISCONNECTED, (event) => {
    console.log('Disconnected from:', event.targetId, event.reason);
});

// Message events
parley.onSystem(SYSTEM_EVENTS.MESSAGE_SENT, (event) => {
    console.log('Sent message:', event.messageType, event.payload);
});

parley.onSystem(SYSTEM_EVENTS.MESSAGE_RECEIVED, (event) => {
    console.log('Received message:', event.messageType, event.payload);
});

// Error events
parley.onSystem(SYSTEM_EVENTS.TIMEOUT, (event) => {
    console.log('Timeout:', event.messageId);
});

parley.onSystem(SYSTEM_EVENTS.ERROR, (event) => {
    console.error('Error:', event.error);
});

Check Message Flow

javascript
// Log all incoming postMessages (before ParleyJS processes them)
window.addEventListener('message', (event) => {
    console.log('Raw postMessage received:', {
        origin: event.origin,
        data: event.data,
        source: event.source,
    });
});

Inspect ParleyJS State

javascript
// Check if connected
console.log('Connected targets:', parley.getConnectedTargets());

// Check registered handlers
console.log('Registered handlers:', parley.getRegisteredHandlers());

Common Mistakes

Mistake: Using wildcard origin

javascript
// DANGEROUS - accepts messages from ANY origin
const parley = Parley.create({
    allowedOrigins: ['*'],
});

Why it's wrong: Security vulnerability - malicious sites can send messages

Correct approach: Always specify exact origins

javascript
const parley = Parley.create({
    allowedOrigins: ['https://trusted-domain.com'],
});

Mistake: Not waiting for connection

javascript
// Wrong - send before connected
const parley = Parley.create({ allowedOrigins: ['https://child.com'] });
parley.connect(iframe.contentWindow, 'child'); // No await!
await parley.send('hello', {}); // Might fail!

// Correct - wait for connection
await parley.connect(iframe.contentWindow, 'child');
await parley.send('hello', {}, { targetId: 'child' });

Mistake: Forgetting to call respond()

javascript
// Wrong - handler doesn't respond
parley.on('get-data', (payload) => {
    const data = fetchData();
    // No respond() call - causes timeout!
});

// Correct
parley.on('get-data', (payload, respond) => {
    const data = fetchData();
    respond({ data });
});

Mistake: Sending non-serializable data

javascript
// Wrong - functions aren't serializable
await parley.send('update', {
    callback: () => console.log('done'), // Error!
});

// Correct - only send plain data
await parley.send('update', {
    message: 'Update complete',
});

Mistake: Not cleaning up

javascript
// Wrong - memory leak
function setupParley() {
    const parley = Parley.create({ allowedOrigins: ['https://child.com'] });
    parley.on('message', handleMessage);
    // Never cleaned up!
}

// Correct - clean up when done
function setupParley() {
    const parley = Parley.create({ allowedOrigins: ['https://child.com'] });
    parley.on('message', handleMessage);

    // Cleanup function
    return () => {
        parley.destroy(); // Remove listeners and cleanup
    };
}

const cleanup = setupParley();
// Later...
cleanup();

Still Having Issues?

If you're still experiencing problems:

  1. Check the examples: See examples/ for working code
  2. Review the API docs: See API Reference for complete documentation
  3. Search existing issues: Check GitHub Issues
  4. Ask for help: Open a new issue with:
    • ParleyJS version
    • Browser and version
    • Minimal reproduction code
    • Error messages
    • Debug logs (with debug: true)

See Also

Back to: Documentation Home

Released under the MIT License.