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

# Troubleshooting

> Blank players, ORIGIN_NOT_ALLOWED, token errors, layout issues — fix them fast.

## Symptom → Cause → Fix

| Symptom                                          | Most likely cause                                          | Fix                                                                                                             |
| ------------------------------------------------ | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| **Blank div, nothing renders**                   | Origin not in allow-list                                   | Add your origin in the dashboard (see [Allowed Origins](#allowed-origins))                                      |
| **`onError` fires: `ORIGIN_NOT_ALLOWED`**        | Same as above                                              | Add the origin                                                                                                  |
| **`onError` fires: `TOKEN_INVALID`**             | Wrong or expired API key                                   | Check `EDPIRE_API_KEY`; ensure no `VITE_`/`NEXT_PUBLIC_` prefix                                                 |
| **`onError` fires: `TOKEN_EXPIRED`**             | Token older than 1 hour                                    | Mint a fresh token each time the learner opens the assessment                                                   |
| **`onError` fires: `TOKEN_USED`**                | Same token used twice                                      | Mint a new token per page load (tokens are single-use)                                                          |
| **`onError` fires: `ASSESSMENT_NOT_FOUND`**      | Wrong ID or unpublished                                    | Verify the UUID; publish the assessment in the dashboard                                                        |
| **`onError` fires: `MAX_ATTEMPTS_REACHED`**      | Learner used all attempts                                  | Show your own "no attempts left" UI; no action needed                                                           |
| **Player loads, then cuts off / looks wrong**    | Container has no height or is constrained                  | Set an explicit height on the container div (see [Container sizing](#container-sizing))                         |
| **Works in `npm run dev`, breaks in production** | Token endpoint was dev-only (Vite middleware)              | Use a real server route — see [Token endpoint not found in production](#token-endpoint-not-found-in-production) |
| **Token endpoint returns 401**                   | `resolveLearner` returned `null`                           | Ensure the user is logged in before the assessment page loads                                                   |
| **Token endpoint returns 400**                   | Missing `assessmentId` in POST body                        | Check that you're POSTing `{ assessmentId }` to the endpoint                                                    |
| **No `onError` call, no player**                 | Container element not found                                | Ensure the container `id`/class exists in the DOM before calling `mount()`                                      |
| **API key leaks to the browser**                 | Used `VITE_EDPIRE_API_KEY` or `NEXT_PUBLIC_EDPIRE_API_KEY` | Remove the prefix — the key must be server-side only                                                            |
| **React StrictMode mounts twice**                | Expected in dev — `useEffect` double-invokes               | This is handled automatically by `<EdpireAssessmentPlayer>` and the `cancelled` flag pattern                    |
| **`NETWORK_ERROR` in `onError`**                 | Can't reach `edpire.com`                                   | Check connectivity; verify `baseUrl` if using staging                                                           |

***

## Allowed Origins

The embed player is scoped to domains you explicitly allow. Without this, the player returns `ORIGIN_NOT_ALLOWED`.

1. Go to **edpire.com → Integrations → Security tab → Allowed Embed Origins**
2. Add every origin your app runs on:
   * `http://localhost:3000` (Next.js dev)
   * `http://localhost:5173` (Vite dev)
   * `https://yourapp.com` (production)
3. Save — changes take effect immediately (no rebuild needed)

<Tip>
  To quickly confirm it's an origin error, add a temporary `onError` log:

  ```typescript theme={null}
  onError: (e) => console.error("[edpire]", e.code, e.message)
  ```
</Tip>

***

## Token endpoint not found in production

If the player worked in `npm run dev` but fails in production with a 404 on `/api/edpire/token`, the token endpoint was probably a **Vite dev-server middleware** — which only runs during `vite dev`, not after `vite build`.

**Fix:** Move the token endpoint to a real server route:

* **Next.js:** `app/api/edpire/token/route.ts` with `createEdpireTokenHandler()` (works in both dev and prod)
* **Vite + SPA:** Add an Express server and proxy `/api` to it — scaffold with `npx --package=@edpire/sdk create-edpire-app vite-express my-app`

***

## API key exposed in browser bundle

If your API key is prefixed with `VITE_` or `NEXT_PUBLIC_`, it compiles into the browser JavaScript. Anyone can read it.

```bash theme={null}
# Wrong
VITE_EDPIRE_API_KEY=edp_live_...      # leaks to browser
NEXT_PUBLIC_EDPIRE_API_KEY=edp_live_  # leaks to browser

# Correct
EDPIRE_API_KEY=edp_live_...           # stays on the server
```

***

## Still stuck?

* Check the [SDK Changelog](/changelog) — a recent version may have changed the API
* See the [full error code reference](/developer/sdk/embedded-player#embederror)
* Contact support at [support@edpire.com](mailto:support@edpire.com)
