Skip to content

Troubleshooting

Common issues and their solutions.

Events Not Appearing in the Dashboard

Check list:

  1. Are the auto-trackers enabled?

    All auto-trackers except console default to false. Verify your config:

    typescript
    track: {
      clicks:     true,  // default: false
      http:       true,  // default: false
      errors:     true,  // default: false
      navigation: true,  // default: false
    }
  2. Is the ingest request succeeding?

    Open the browser DevTools → Network tab → filter for _tracker. Look for the POST /_tracker/events request. A non-2xx response causes the batch to be re-queued (events eventually appear after retry) or lost.

  3. Is track.level filtering events out?

    track.level: 'info' (default) discards 'debug' events before they enter the queue. If you're testing with 'debug' level custom events, either lower track.level or use { level: 'info' } in tracker.track().

  4. Is autoInit: false and tracker.init() was never called?

    With autoInit: false, most tracker.* calls are silently dropped until tracker.init() is called (the exception is tracker.group(), which always returns a valid ID). Check the browser console for [tracker] messages.

  5. Is the plugin enabled: false?

    Check if enabled is conditionally set to false in your environment.


Dashboard Shows "Backend Offline"

The red status dot in the dashboard header means the pingEndpoint is unreachable.

By mode:

ModeBackendWhat to check
middlewareVite dev serverIs vite dev running? Reload the terminal.
httpYour backendIs storage.pingEndpoint reachable from the browser? Check CORS headers.
websocketYour backendIs storage.pingEndpoint reachable? Is the WS server running?

window.__TRACKER_CONFIG__ not found

This error from the dashboard means the Vite dev server is not running or the page was opened without going through Vite.

  • Always open the app through http://localhost:5173 (not as file:///...)
  • Ensure Vite's dev server is running (pnpm dev)
  • If using a different port, check viteConfig.server.port

No Log Files Created

  1. Has Vite been started at least once?

    Log directories are created in the buildStart hook (triggered by vite dev or vite build).

  2. Does the process have write permission?

    Check filesystem permissions on the log directory path.

  3. Is logging.level too high?

    If logging.level: 'error' and no error events are being tracked, the file exists but may be empty.

  4. Is enabled: false?

    When the plugin is disabled, no logger worker is started and no files are created.


HTTP Bodies Are [REDACTED]

The built-in redaction pipeline is intentional and cannot be disabled for the built-in patterns. Keys matching password, token, secret, card, cvv, iban, and others are always replaced with '[REDACTED]'.

If you're seeing redaction on keys you didn't expect, check if the key name contains any substring from the built-in pattern list (case-insensitive).

To add more keys: use http.redactKeys. You cannot remove built-in keys.


tracker.track() Calls Are Silently Dropped

The tracker proxy silently drops all calls until tracker.init() is called.

Checklist:

  1. Is autoInit: false set? → Call tracker.init() manually.
  2. Is autoInit: true (default) but enabled: false? → No-op plugin.
  3. Is the call happening in SSR/Node.js context? → The client API is browser-only.

Debug tip: Add a temporary console.log before and after tracker.init() to confirm it's being called:

typescript
console.log('[debug] calling tracker.init')
tracker.init(() => localStorage.getItem('userId'))
console.log('[debug] tracker.init done')

Production Build Fails: writeEndpoint Required

[vite-plugin-monitor] Production build requires storage.mode = "http" with a valid writeEndpoint.

This error means you ran vite build with storage.mode: 'auto' and no writeEndpoint. Fix:

typescript
storage: {
  mode:          'http',          // ← set explicitly
  writeEndpoint: 'https://...',   // ← required
}

Or disable the plugin for builds that don't need tracking:

typescript
enabled: process.env.NODE_ENV !== 'production',

Dashboard Not Found After vite build

If dashboard.includeInBuild: true but the dashboard is missing from dist/:

  1. Did you run pnpm build:dashboard before vite build?

    bash
    pnpm build:dashboard   # always first
    pnpm build             # then your app build
  2. Check the terminal for warnings:

    [vite-plugin-monitor] includeInBuild is true but dashboard dist not found at .../dashboard.
    Run 'pnpm build:dashboard' before 'vite build'.

The build does not fail when the dashboard is missing — it logs a warning and skips the copy.


Console Output Not Captured

By default, console tracking is true (all 19 methods). If console events aren't appearing:

  1. Are console calls happening before the setup script runs?

    The setup script is injected at head-prepend, but very early console.* calls (during module evaluation of your bundler's runtime) may precede it. This is generally rare.

  2. Are the calls matching ignorePatterns?

    Default ignored patterns: ['[vite]', '[HMR]', '[tracker]']. Vite's own console output is intentionally excluded.

  3. Is console: false set?

    Check your track.console config.

  4. Is track.level filtering them out?

    console.debug() calls are emitted at 'debug' level. With track.level: 'info' (default), debug events are discarded.


track.userId Function Not Working

[vite-plugin-monitor] userId function references undefined variable

The track.userId function is serialized via .toString() and injected as a string into index.html. It cannot reference module-level variables, imports, or closures.

typescript
// ❌ This breaks — authStore is not available in the injected script
import { authStore } from './store'
userId: () => authStore.getState().userId

// ✅ Use browser globals only
userId: () => window.__auth?.userId ?? null
userId: () => localStorage.getItem('userId')
userId: () => sessionStorage.getItem('user_id')

// ✅ Or use autoInit: false + tracker.init() in app code

TypeScript Errors on TrackerEvent.payload

The payload field is a discriminated union. Narrow it with the type field:

typescript
// ❌ TypeScript error: Property 'tag' does not exist on type 'EventPayload'
const tag = event.payload.tag

// ✅ Narrow first
if (event.type === 'click') {
  const tag = event.payload.tag  // OK — ClickPayload
}

High Memory Usage in Middleware Mode

The ring buffer defaults to 500,000 events. Each event is roughly 1–2 KB in memory (depending on headers/bodies), so the maximum memory footprint is approximately 500 MB–1 GB.

Reduce if needed:

typescript
storage: {
  maxBufferSize: 10000,  // keep only 10k events in memory
}

The ring buffer automatically evicts the oldest events (FIFO) when capacity is exceeded.


Slow Ingest Requests or Proxy Interference in Middleware Mode

If you run the application via vite preview with a proxy configured, and backend calls intermittently fail with CONNECTION_RESET or remain pending for a long time, the likely cause is the size of the ingest request body.

When http.captureResponseBody, http.captureRequestBody, or http.captureResponseHeaders are enabled with a large batchSize, each POST to /_tracker/events can carry several hundred KB of payload. This keeps the socket occupied long enough to interfere with concurrent proxied backend calls sharing the same Vite server.

Mitigations, in order of effectiveness:

  1. Request body compression (built-in): The client automatically gzip-compresses the ingest body when the browser supports the CompressionStream API. This reduces payload size by 80–95% with no configuration needed. Ensure your Vite version is recent enough to include this feature.

  2. Reduce batchSize: Lower the number of events sent per flush:

typescript
   storage: { batchSize: 10 }  // default: 25
  1. Reduce maxBodySize: Limit how many bytes of request/response bodies are captured:
typescript
   track: {
     http: {
       captureResponseBody: true,
       maxBodySize: 512,  // default: 2048
     }
   }
  1. Disable body capture for noisy endpoints: Use ignoreUrls to exclude endpoints that return large payloads:
typescript
   track: {
     http: {
       captureResponseBody: true,
       ignoreUrls: [/\/api\/large-endpoint/],
     }
   }
  1. Stagger flushInterval: If your app has polling calls on a fixed interval, set flushInterval to a value that doesn't align with the polling cadence to reduce the chance of concurrent requests:
typescript
   storage: { flushInterval: 7000 }  // if polling is every 10s, avoid 5000 or 10000

Getting Help

If none of the above resolves your issue:

  1. Check the GitHub Issues for existing reports
  2. Open a new issue with your vite.config.ts snippet and the browser console output
  3. Email: admin@ndria.dev

Released under the MIT License.