Debugging Guide#

When events aren't showing up in your Pulsora dashboard, use these systematic debugging steps to diagnose and fix the issue quickly.

Quick Diagnosis Checklist#

Before diving deep, check these common issues:

  • Debug mode enabled in browser console
  • Network requests reaching pulsora.co/api/ingest with 202 response
  • Correct API token being used (public pub_ for frontend, secret sec_ for backend)
  • Domain whitelisted in Pulsora dashboard settings
  • No ad blockers or privacy extensions blocking requests
  • CSP headers allow connections to pulsora.co

1. Enable Debug Mode#

const pulsora = new Pulsora();
pulsora.init({
  apiToken: 'pub_123',
  debug: true,
});

Debug mode logs every pageview, event, identify, retry, and failure:

[Pulsora] Initialized
[Pulsora] Pageview tracked: /pricing
[Pulsora] Retry in 1000ms (attempt 2)

Disable debug in production to avoid noisy consoles.

2. Check the Network Tab#

  • Filter requests by ingest.
  • Ensure POST https://pulsora.co/api/ingest returns 202.
  • Open a request to confirm the payload contains the right event name and metadata.

If you see net::ERR_BLOCKED_BY_CLIENT, an ad blocker is interfering. Pulsora uses a neutral domain, but some users run aggressive blocklists—see fallback strategies below.

3. Verify CSP Configuration#

If requests are blocked by Content-Security-Policy:

connect-src https://pulsora.co;
script-src https://cdn.pulsora.co;

4. Monitor Retry Logs#

Retries usually point to transient outages or missing tokens:

[Pulsora] Send failed HTTP 401

Double-check that you use the correct public token in the frontend and secret token on the backend.

5. React Hook Issues#

  • usePulsora undefined — Confirm the component is wrapped by <PulsoraProvider>.
  • Duplicate pageviews — Ensure usePageview is invoked once per navigation (e.g., a top-level analytics bridge).

6. Backend Troubleshooting#

  • Check server logs for Revenue exceptions (validation errors list missing fields).
  • If a webhook handler fails, respond with non-2xx and let the payment processor retry after you fix the issue.
  • Confirm that the backend sends requests with X-API-Token: sec_....

7. Ad Blockers & Privacy Extensions#

  • Pulsora URLs are intentionally neutral, but some extensions block any analytics endpoint.
  • Mitigation options:
    • Use server-side tracking for critical events (e.g., call /api/ingest from your backend).
    • Provide fallbacks in your product analytics dashboards (e.g., flag blocked_by_client errors).

8. Platform-Specific Debugging#

Next.js Debugging#

App Router (Next.js 13+):

// app/providers.tsx
'use client';

import { PulsoraProvider } from '@pulsora/react';
import { useEffect } from 'react';

export function Providers({ children }) {
  // Debug: Check if provider mounts
  useEffect(() => {
    console.log('[Debug] PulsoraProvider mounted');
    console.log(
      '[Debug] API Token:',
      process.env.NEXT_PUBLIC_PULSORA_TOKEN?.slice(0, 10) + '...',
    );
  }, []);

  return (
    <PulsoraProvider
      config={{
        apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
        debug: true, // Enable for development
      }}
    >
      {children}
    </PulsoraProvider>
  );
}

Common Next.js issues:

  1. Environment variables not available:
# ❌ Wrong - not accessible in browser
PULSORA_TOKEN=pub_xxx

# ✅ Correct - prefix with NEXT_PUBLIC_
NEXT_PUBLIC_PULSORA_TOKEN=pub_xxx
  1. Hydration mismatches:
// Use 'use client' directive
'use client';

import { usePageview } from '@pulsora/react';

function Analytics() {
  usePageview(); // Safe on client
  return null;
}
  1. Route changes not tracked:
// Add pageview tracker at root layout
'use client';

import { usePathname } from 'next/navigation';
import { usePageview } from '@pulsora/react';
import { useEffect } from 'react';

export function PageviewTracker() {
  const pathname = usePathname();

  useEffect(() => {
    console.log('[Debug] Route changed:', pathname);
  }, [pathname]);

  usePageview({ trigger: pathname });

  return null;
}

React Debugging#

Hook order issues:

// ❌ Wrong - hooks called conditionally
function MyComponent({ isEnabled }) {
  if (isEnabled) {
    const trackEvent = useEvent(); // Breaks React rules!
  }
}

// ✅ Correct - hooks at top level
function MyComponent({ isEnabled }) {
  const trackEvent = useEvent();

  if (isEnabled) {
    // Use the hook result conditionally
    trackEvent('feature_enabled');
  }
}

Provider context issues:

// Debug: Check if provider is accessible
import { usePulsora } from '@pulsora/react';

function DebugComponent() {
  try {
    const pulsora = usePulsora();
    console.log('[Debug] Pulsora instance:', pulsora);
    console.log('[Debug] Is initialized:', !!pulsora);
  } catch (error) {
    console.error('[Debug] Provider error:', error);
  }

  return null;
}

Vue.js Debugging#

// plugins/pulsora.js
import { Pulsora } from '@pulsora/core';

export default {
  install(app, options) {
    console.log('[Debug] Installing Pulsora plugin');
    console.log('[Debug] API Token:', options.apiToken?.slice(0, 10) + '...');

    const pulsora = new Pulsora();
    pulsora.init({
      apiToken: options.apiToken,
      debug: true,
    });

    // Make available globally
    app.config.globalProperties.$pulsora = pulsora;

    // Debug: Test tracking
    console.log('[Debug] Testing event tracking...');
    pulsora.event('plugin_installed');
  },
};

9. Common Error Messages#

"Failed to fetch"#

Error in console:

POST https://pulsora.co/api/ingest net::ERR_FAILED

Causes:

  1. No internet connection
  2. Ad blocker or privacy extension
  3. Corporate firewall blocking analytics domains
  4. CSP blocking the request

Solutions:

// 1. Test basic connectivity
fetch('https://pulsora.co/api/ingest', {
  method: 'HEAD',
})
  .then(() => {
    console.log('[Debug] Can reach Pulsora API');
  })
  .catch((err) => {
    console.error('[Debug] Cannot reach Pulsora:', err);
  });

// 2. Check for ad blockers
if (
  typeof window.pulsora === 'undefined' &&
  window.location.hostname !== 'localhost'
) {
  console.warn('[Debug] Pulsora may be blocked by ad blocker');
}

// 3. Server-side tracking fallback
async function trackEventWithFallback(eventName, props) {
  try {
    // Try client-side first
    await pulsora.event(eventName, props);
  } catch (error) {
    // Fall back to server-side endpoint
    await fetch('/api/track', {
      method: 'POST',
      body: JSON.stringify({ event: eventName, props }),
    });
  }
}

"Invalid API token"#

Error: 401 Unauthorized or 403 Forbidden

Debug steps:

// Check token format
const token = process.env.NEXT_PUBLIC_PULSORA_TOKEN;

console.log('[Debug] Token format:', {
  exists: !!token,
  length: token?.length,
  prefix: token?.slice(0, 4),
  isPublic: token?.startsWith('pub_'),
  isSecret: token?.startsWith('sec_'),
});

// Frontend should use public tokens
if (!token?.startsWith('pub_')) {
  console.error('[Debug] Frontend should use public token (pub_xxx)');
}

"net::ERR_BLOCKED_BY_CLIENT"#

Error: Request blocked by browser extension

Detection:

// Detect ad blocker
async function detectAdBlocker() {
  const testUrl = 'https://pulsora.co/api/ingest';

  try {
    await fetch(testUrl, { method: 'HEAD', mode: 'no-cors' });
    console.log('[Debug] No ad blocker detected');
    return false;
  } catch (error) {
    if (error.message.includes('blocked')) {
      console.warn('[Debug] Ad blocker detected!');
      return true;
    }
    throw error;
  }
}

// Use on app startup
detectAdBlocker().then((isBlocked) => {
  if (isBlocked) {
    // Show message or use server-side tracking
    console.log('[Debug] Switching to server-side tracking');
  }
});

Mitigation:

// Server-side tracking proxy
// app/api/track/route.ts
import { Pulsora } from '@pulsora/core';

const pulsora = new Pulsora();
pulsora.init({
  apiToken: process.env.PULSORA_PUBLIC_TOKEN!,
  // Use custom endpoint to bypass blockers
  endpoint: process.env.PULSORA_ENDPOINT
});

export async function POST(req: Request) {
  const { event, props, fingerprint, sessionId } = await req.json();

  await pulsora.event(event, props);

  return Response.json({ success: true });
}

10. Performance Debugging#

Slow Initialization#

Issue: Pulsora takes too long to initialize

Debug:

console.time('[Debug] Pulsora Init');

const pulsora = new Pulsora();
await pulsora.init({
  apiToken: 'pub_xxx',
  debug: true,
});

console.timeEnd('[Debug] Pulsora Init');

// Should complete in <100ms typically

Optimization:

// Use dynamic import for code splitting
async function initAnalytics() {
  const { Pulsora } = await import('@pulsora/core');
  const pulsora = new Pulsora();
  await pulsora.init({ apiToken: 'pub_xxx' });
  return pulsora;
}

// Initialize on user interaction or after page load
document.addEventListener('DOMContentLoaded', () => {
  setTimeout(() => initAnalytics(), 1000);
});

High Network Usage#

Issue: Too many requests to Pulsora API

Debug:

// Count Pulsora requests
let requestCount = 0;
const originalFetch = window.fetch;

window.fetch = function (...args) {
  if (args[0]?.includes('pulsora.co')) {
    requestCount++;
    console.log(`[Debug] Pulsora request #${requestCount}:`, args);
  }
  return originalFetch.apply(this, args);
};

// Check after 30 seconds
setTimeout(() => {
  console.log(`[Debug] Total Pulsora requests: ${requestCount}`);
  // Should be <10 for typical single-page app
}, 30000);

Common causes:

  • Multiple usePageview() calls
  • Event tracking in render loop

Fix:

// ❌ Wrong - tracks on every render
function Component() {
  const trackEvent = useEvent();
  trackEvent('page_view'); // Runs every render!
}

// ✅ Correct - tracks once on mount
function Component() {
  usePageview(); // Tracks once
}

11. Production Debugging#

Enable Selective Debug Mode#

Only enable debug mode for admins or specific users:

const isDev = process.env.NODE_ENV === 'development';
const isAdmin = user?.role === 'admin';
const debugParam = new URLSearchParams(window.location.search).has('debug');

pulsora.init({
  apiToken: 'pub_xxx',
  debug: isDev || isAdmin || debugParam,
});

Monitor Failed Requests#

// Log failed tracking attempts
const pulsora = new Pulsora();

// Wrap methods to catch errors
const originalEvent = pulsora.event.bind(pulsora);
pulsora.event = async function (name, props) {
  try {
    await originalEvent(name, props);
  } catch (error) {
    // Send to error tracking service
    console.error('[Pulsora Error]', { name, props, error });
    errorTracker.captureException(error, {
      extra: { event: name, props },
    });
  }
};

Health Check Endpoint#

// app/api/health/analytics/route.ts
import { Pulsora } from '@pulsora/core';

export async function GET() {
  const pulsora = new Pulsora();

  try {
    await pulsora.init({
      apiToken: process.env.PULSORA_PUBLIC_TOKEN!
    });

    // Test tracking
    await pulsora.event('health_check');

    return Response.json({
      status: 'healthy',
      service: 'pulsora',
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    return Response.json({
      status: 'unhealthy',
      service: 'pulsora',
      error: error.message
    }, { status: 503 });
  }
}

12. Support Checklist#

When escalating to support, include:

Required Information#

  1. Environment details:

    • Browser (Chrome, Firefox, Safari + version)
    • OS (macOS, Windows, Linux)
    • Framework (React, Next.js, Vue + version)
    • Pulsora package versions
  2. Console output:

    • Screenshots with debug mode enabled
    • Full error messages and stack traces
    • Any warning messages
  3. Network details:

    • Request/response from Network tab
    • Request headers (especially X-API-Token)
    • Response status code and body
  4. Code snippets:

    • Pulsora initialization code
    • Event tracking code that's failing
    • Provider/hook usage if using React
  5. Timing information:

    • When the issue started (UTC timezone)
    • Frequency (always, intermittent, specific users)
    • Any recent changes to code or infrastructure

Debug Bundle Export#

// Generate debug bundle for support
function exportDebugInfo() {
  const debug = {
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent,
    url: window.location.href,
    packages: {
      core: '@pulsora/core@1.0.0', // Get from package.json
      react: '@pulsora/react@1.0.0',
    },
    config: {
      hasToken: !!process.env.NEXT_PUBLIC_PULSORA_TOKEN,
      tokenPrefix: process.env.NEXT_PUBLIC_PULSORA_TOKEN?.slice(0, 4),
      debug: pulsora.config.debug,
    },
    network: {
      // Check connectivity
      canReachAPI: false, // Set after testing
    },
  };

  console.log('Debug Bundle:', JSON.stringify(debug, null, 2));

  // Copy to clipboard
  navigator.clipboard.writeText(JSON.stringify(debug, null, 2));
  console.log('Debug bundle copied to clipboard');
}

// Run in console when debugging
exportDebugInfo();

Next Steps#