> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tappd.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshoot Tappd Web SDK — Common Errors and Fixes

> Step-by-step fixes for the most common Tappd Web SDK problems — initialization failures, missing events, CORS, TypeScript errors, and more.

Most Web SDK issues fall into a small set of categories: misconfiguration, missing identification, storage restrictions, or network problems. Work through the relevant accordion below, then check the **Common Error Messages** table at the bottom if you need a quick reference.

***

<Accordion title="SDK Not Initializing — 'App ID is required'">
  **Symptoms:**

  * Console shows `"App ID is required"` on page load
  * No events appear in the Tappd dashboard

  **Cause:** The `appId` field is missing, empty, or `undefined` in your config object.

  <CodeGroup>
    ```javascript Wrong theme={null}
    // ❌ No appId provided
    const tappd = new TappdSDK({});

    // ❌ appId is undefined (env var not set)
    const tappd = new TappdSDK({
      appId: process.env.TAPPD_APP_ID // undefined if not configured
    });
    ```

    ```javascript Correct theme={null}
    // ✅ Hard-coded for quick testing
    const tappd = new TappdSDK({
      appId: 'YOUR_APP_ID'
    });

    // ✅ Environment variable — verify it's actually set
    const tappd = new TappdSDK({
      appId: process.env.NEXT_PUBLIC_TAPPD_APP_ID // must not be undefined
    });
    ```
  </CodeGroup>

  **Checklist:**

  1. Copy your App ID from the Tappd dashboard — Settings → App ID.
  2. Confirm the environment variable is defined in your `.env` file and your build tool exposes it to the client.
  3. Verify the SDK script or npm package loads fully before your initialization code runs.
</Accordion>

<Accordion title="Events Not Tracking — No Events in Dashboard">
  **Symptoms:**

  * `track()` resolves without errors
  * The Tappd dashboard shows no events

  **Step 1 — Enable debug mode** to see what the SDK is doing:

  ```javascript theme={null}
  const tappd = new TappdSDK({
    appId: 'YOUR_APP_ID',
    debug: true
  });
  // Console output examples:
  // [Tappd SDK] Initialized with App ID: a1b2c3d4...
  // [Tappd SDK] New session started: sess_abc123
  // [Tappd SDK] Event tracked: purchase
  ```

  **Step 2 — Check the network tab:**

  1. Open DevTools → **Network**.
  2. Filter by `track` or `api`.
  3. Confirm requests are sent and check their HTTP status codes.
  4. A `4xx` response points to a config problem; a `5xx` indicates a server issue.

  **Step 3 — Verify your API URL:**

  ```javascript theme={null}
  const tappd = new TappdSDK({
    appId: 'YOUR_APP_ID',
    apiUrl: 'https://sdk.gotappd.com/api/v1/sdk' // default — only override if needed
  });
  ```

  **Step 4 — Look for CORS errors:**

  * Check the browser console for `"Access-Control-Allow-Origin"` messages.
  * See the **CORS Errors** accordion below for fixes.
</Accordion>

<Accordion title="Anonymous Data Not Merging After identify()">
  **Symptoms:**

  * Events tracked before `identify()` don't appear on the user's profile in the dashboard
  * User journeys look incomplete — the pre-login funnel is missing

  **Cause:** `identify()` was never called, was called without a valid identifier, or was called after the session that contained the anonymous events had expired.

  <CodeGroup>
    ```javascript Wrong theme={null}
    // ❌ Tracking events but never identifying the user
    await tappd.track('page_view');
    await tappd.track('button_click');
    // These events stay anonymous forever
    ```

    ```javascript Correct theme={null}
    // ✅ Identify the user — previous anonymous events merge automatically
    await tappd.track('page_view');
    await tappd.track('button_click');

    await tappd.identify({
      external_id: 'user_123', // must be valid, non-empty
      email: 'jane@example.com'
    });
    // All previous anonymous events are now linked to user_123
    ```
  </CodeGroup>

  **Common mistakes:**

  * Passing `null`, `undefined`, or an empty string as `external_id` — the SDK throws `"external_id is required in attributes"`.
  * Calling `identify()` in a later session without the same `external_id` — the SDK creates a new profile instead of merging.
  * Calling `reset()` before `identify()` — this clears the anonymous ID and breaks the merge chain.

  **Verify your anonymous ID is stable:**

  ```javascript theme={null}
  // Check this before and after identify() — the value should be the same
  const anonymousId = tappd.getAnonymousId();
  console.log('Anonymous ID:', anonymousId);
  ```
</Accordion>

<Accordion title="Session Issues — No Session ID or Sessions Restarting">
  **Symptoms:**

  * `getSessionId()` returns `null`
  * Sessions restart far too frequently

  **Fix 1 — Increase the session timeout** if sessions expire too quickly:

  ```javascript theme={null}
  const tappd = new TappdSDK({
    appId: 'YOUR_APP_ID',
    sessionTimeout: 60 // minutes — default is 30
  });
  ```

  **Fix 2 — Verify the session ID after initialization:**

  ```javascript theme={null}
  const sessionId = tappd.getSessionId();
  console.log('Session ID:', sessionId);
  // Should be a non-null string like sess_abc123def456
  ```

  <Note>
    Sessions automatically pause when the browser tab is hidden and resume when it becomes visible again. This is expected behavior — it does **not** create a new session.
  </Note>
</Accordion>

<Accordion title="SPA Navigation Not Tracking — Only First Page View Captured">
  **Symptoms:**

  * Only the initial page load registers as a page view
  * Route changes in your single-page app are not tracked

  **Fix 1 — Confirm `autoTrack` is enabled** (it is `true` by default):

  ```javascript theme={null}
  const tappd = new TappdSDK({
    appId: 'YOUR_APP_ID',
    autoTrack: true
  });
  ```

  The SDK intercepts `history.pushState` to detect navigation. If your router overrides this in a non-standard way, use the manual approach below.

  **Fix 2 — Track route changes manually** in your router's navigation hooks:

  ```javascript theme={null}
  // Vue Router example
  router.beforeEach((to, from) => {
    tappd.trackPageView(to.fullPath, {
      from: from.fullPath,
      routeName: to.name
    });
  });

  // React Router v6 example (inside a component that renders on every route)
  useEffect(() => {
    tappd.trackPageView(window.location.pathname);
  }, [location.pathname]);
  ```
</Accordion>

<Accordion title="localStorage Issues — New Anonymous ID on Every Page Load">
  **Symptoms:**

  * Each page load produces a different anonymous ID
  * Users appear as new visitors on every visit

  **Cause:** The SDK stores the anonymous ID in `localStorage`. If storage is unavailable, blocked, or cleared between visits, a new ID is generated every time.

  **Check 1 — Verify `localStorage` is available:**

  ```javascript theme={null}
  if (typeof Storage === 'undefined') {
    console.error('localStorage is not supported in this environment.');
  }
  ```

  **Check 2 — Review your browser environment:**

  * Private/incognito mode limits or blocks `localStorage` in some browsers — this is expected.
  * Check for browser extensions (ad blockers, privacy tools) that clear or block storage.
  * Confirm no other script in your app calls `localStorage.clear()` on page load.
</Accordion>

<Accordion title="CORS Errors — 'Access-Control-Allow-Origin' Failures">
  **Symptoms:**

  * Browser console shows `"CORS policy"` or `"Access-Control-Allow-Origin"` errors
  * All network requests to the Tappd API fail

  **Fix 1 — Verify the default endpoint is reachable:**

  Open a new tab and navigate to `https://sdk.gotappd.com` to confirm there is no network-level block (firewall, VPN, or DNS issue).

  **Fix 2 — Check your custom domain setup:**

  If you're using a custom `apiUrl`, your domain must be allowlisted on the Tappd side. Contact Tappd support and provide:

  * Your production domain(s)
  * The custom `apiUrl` you are using

  <Warning>
    Do not set `apiUrl` to a custom value unless Tappd support has explicitly provided one for your account. Pointing to the wrong endpoint is the most common cause of CORS failures.
  </Warning>
</Accordion>

<Accordion title="TypeScript Errors — Type Mismatches or Missing Types">
  **Symptoms:**

  * TypeScript compiler reports errors when importing from `@tappd/web-sdk`
  * IntelliSense does not recognize SDK types

  **Fix — Use the named imports from the package:**

  ```typescript theme={null}
  import { TappdSDK, TappdConfig, CustomerAttributes } from '@tappd/web-sdk';

  const config: TappdConfig = {
    appId: 'YOUR_APP_ID',
    debug: true
  };

  const tappd = new TappdSDK(config);

  const userAttributes: CustomerAttributes = {
    external_id: 'user_123',
    email: 'jane@example.com',
    name: 'Jane Smith'
  };

  await tappd.identify(userAttributes);
  ```

  If your project still shows type errors, install the Node type definitions:

  ```bash theme={null}
  npm install --save-dev @types/node
  ```
</Accordion>

<Accordion title="Build Tool Issues — Webpack or Vite Not Resolving the SDK">
  **Symptoms:**

  * Build fails with a module resolution error
  * SDK import throws at runtime in the browser

  **Fix — Use the correct import style for your build tool:**

  <CodeGroup>
    ```javascript ES Module (Webpack 5, Vite, Rollup) theme={null}
    import { TappdSDK } from '@tappd/web-sdk';
    ```

    ```javascript CommonJS (Webpack 4, Node.js scripts) theme={null}
    const { TappdSDK } = require('@tappd/web-sdk');
    ```
  </CodeGroup>

  **Bundle size note:** The SDK weighs approximately **10 KB** — it should not cause any meaningful bundle size increase. Modern bundlers automatically tree-shake unused exports, so you only pay for what you import.
</Accordion>

***

## Getting Help

If you've worked through the accordions above and still can't resolve the issue, follow these steps before reaching out to support:

<Steps>
  <Step title="Check the browser console">
    Look for red error messages when the SDK initializes or when you call a method. Copy the full error text.
  </Step>

  <Step title="Enable debug mode">
    Set `debug: true` in your `TappdSDK` config and reproduce the issue. The SDK logs every action to the console — capture the full output.
  </Step>

  <Step title="Inspect network requests">
    Open DevTools → **Network**, filter by `api` or `tappd`, and check the status codes and response bodies of failed requests.
  </Step>

  <Step title="Confirm your SDK version">
    Run `npm list @tappd/web-sdk` and verify you're on the latest release. Update if needed: `npm install @tappd/web-sdk@latest`.
  </Step>

  <Step title="Contact support">
    Reach out with the following information so the team can reproduce and fix your issue quickly:

    * The full error message from the console
    * Your browser name and version
    * Your `@tappd/web-sdk` version
    * The debug log output from step 2
  </Step>
</Steps>

***

## Common Error Messages

| Error Message                             | Cause                                                                | Fix                                                                                   |
| ----------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `"App ID is required"`                    | `appId` is missing or empty in the config                            | Provide a valid `appId` from the Tappd dashboard                                      |
| `"User must be identified first"`         | A method that requires identification was called before `identify()` | Call `identify()` with at least one valid identifier first                            |
| `"external_id is required in attributes"` | `external_id` passed as `null`, `undefined`, or `""`                 | Pass a non-empty string for `external_id`                                             |
| `"Request failed: ..."`                   | Network failure or API-level error                                   | Check your `apiUrl`, internet connection, and CORS configuration                      |
| `"Push notifications not supported"`      | Browser does not support the Push API                                | Only prompt for push in supported browsers (check `'PushManager' in window`)          |
| `"Notification permission denied"`        | User has blocked notifications at the browser level                  | Prompt users before they deny, or guide them to reset permissions in browser settings |
