VoidhashDocs
DocumentationAPI reference
Dashboard
Get started
Introduction
Quickstart
Setup
Setup overview
Create a project
Products and perks
Paywall locations
CLI
Paywall components
Components
Develop a component
Editor panels
Component definition
Publishing
Installing and capabilities
SDKs
Swift
Kotlin
React Native
Web
Store setup
Connect the App Store
Connect Google Play
Connect Stripe
API libraries
Node.js
PHP
Rust
Go
Resources
MCP server
Meta ads
Migrate in observer mode
Delete a person's data
Web SDK
Web quickstart
Identify users
Track events
Page views and attribution
Stripe revenue
Web-to-app handoff
First-party proxy
Feature flags
Troubleshooting

Troubleshooting

Diagnose missing events, duplicate page views, lost visitors, and SDK errors.

Start with diagnostics: they tell you what the SDK is doing in the background, where it never throws. Log them while you integrate:

const voidhash = createVoidhashClient({
  onDiagnostic: (diagnostic) => console.debug("[voidhash]", diagnostic),
  publishableKey: "vh_pk_…",
});

No events arrive

Check these in order.

  1. Is initialize() called? In React, the provider calls it. Elsewhere, call it once per page.
  2. Is analytics consent granted? With analyticsConsent: "pending", nothing is sent until you call setAnalyticsConsent("granted"). The ANALYTICS_CONSENT_DENIED diagnostic says so.
  3. Is the publishable key right? A rejected key pauses all requests and reports AUTHENTICATION_FAILED once. Copy the key again from Settings → API keys in Studio.
  4. Does a Content Security Policy block the requests? Add https://api.voidhash.com, or your proxy's origin, to connect-src.
  5. Does an ad blocker block them? In the Network tab, look for blocked requests to /i/v1/batch. Set up a first-party proxy.
  6. Are you testing with a headless browser? Events from browsers that identify themselves as headless or as test tools, such as headless Chrome (including Playwright's and Puppeteer's default) or Cypress, are discarded as bot traffic. Test in a regular browser window.
  7. Is the event turned on? Self-hosted projects store $pageview and $sign_out only after they are turned on under Settings → Events.

Events are sent every 5 seconds, so allow a few seconds before they appear in Activity → Analytics events. Call flushAnalytics() to send them at once; it resolves with { flushed, pending, lastError }.

Page views are counted twice

  • Your app calls page() on every route change while automatic page views are on. Remove the calls, or set analytics: { capturePageViews: false }.
  • Two clients for the same project run on the page, for example one from a module and one from a VoidhashProvider with config. Create one client and pass it to the provider with client.

React Strict Mode does not double page views: the provider keeps one client through its remount.

Returning visitors show up as new visitors

  • The browser cleared storage. The anonymous ID lives in localStorage. Private windows, "clear on exit" settings, and blocked storage start a new visitor every time. Safari and other browsers with tracking prevention may also delete storage a script wrote after about seven days without a visit. Identify signed-in users, so each visit is linked to them again.
  • The publishable key changed. The SDK keeps its saved state per project key, so another key starts new visitors.
  • Another subdomain. localStorage is per origin, so www.example.com and app.example.com have different anonymous visitors until the visitor is identified on both.

A purchase is not credited to a campaign

  • The Checkout Session or customer carries no Voidhash ID. See Stripe revenue.
  • The ID was read before identify() while the visitor was signed in, so the purchase landed on the anonymous visitor. It moves to the user when that visitor is identified.
  • The visitor arrived without campaign parameters, or declined analytics consent, so they have no first touch.
  • The ad's URL does not carry the campaign IDs. See Meta ads.

Errors

ErrorCause and fix
VoidhashNotInitializedErrorA method ran before initialize() was called. Call it first, or use the provider.
VoidhashDestroyedErrorThe client was destroyed, for example by a provider that unmounted. Create a new client.
VoidhashConfigurationErrorAn option is invalid, such as a blank publishableKey.
initialize() needs a browserinitialize() ran on a server. Call it from client-side code, such as an effect, or render the provider.
VoidhashInvalidArgumentErroridentify() got a blank ID or one that starts with vh:anon:, or track() a blank event name. Nothing was changed.
useVoidhash must be used within a VoidhashProviderA hook rendered outside the provider.

Diagnostics

Each diagnostic has a stable code, a kind for routing, the SDK operation, whether the SDK will retry on its own, the httpStatus when there was a response, and a readable message.

CodeKindMeaning
TRANSPORT_FAILEDtransportA request failed or timed out. Events and identity writes are retried.
ANALYTICS_UNEXPECTED_STATUStransportVoidhash answered with an unexpected status. The batch stays queued.
MALFORMED_CAPTURE_RESPONSEtransportVoidhash accepted a batch but its answer could not be read.
KEEPALIVE_EVENT_TOO_LARGEtransportAn event did not fit the request sent while leaving the page. It is sent on the next visit.
IDENTITY_WRITE_REFUSEDtransportVoidhash refused an identity link for good. It was dropped; the browser keeps its identity.
IDENTITY_WRITE_DROPPEDevictionMore than 20 users identified while Voidhash could not be reached; the oldest link was dropped.
ANALYTICS_EVENT_DROPPEDevictionAn event was dropped: the queue was full, or Voidhash refused it for good.
CIRCUIT_OPENEDbreakerRepeated failures paused requests to Voidhash. They resume on their own.
CIRCUIT_OPENbreakerA request was skipped while requests are paused.
AUTHENTICATION_FAILEDauthThe publishable key was rejected. Requests pause until initialize(), identify(), signOut(), or reset(), or until one request is tried again a minute later when the tab is visible.
CACHE_READ_FAILEDcacheBrowser storage could not be read. The SDK continues in memory.
CACHE_WRITE_FAILEDcacheBrowser storage refused a write, for example when full. The queue stays in memory.
ANALYTICS_CONSENT_DENIEDconsentAnalytics consent is not granted, so analytics are not recorded. Reported once.

Report auth diagnostics to your monitoring: they mean no data reaches Voidhash.

PreviousFeature flagsNextConnect the App Store

On this page

No events arrivePage views are counted twiceReturning visitors show up as new visitorsA purchase is not credited to a campaignErrorsDiagnostics