Troubleshooting
This guide covers the most common issues developers encounter when setting up and running Contentful Personalization, along with their causes and solutions. Use this guide to check your setup and troubleshoot those issues.
Personalization silently does nothing
Content types were not extended
- Symptoms:
Experiences are configured in Contentful, but the front-end always shows the baseline. The nt_experiences field does not appear on your entries.
- Cause:
The Contentful Personalization app was installed, but the content types were never configured for personalization. Without the nt_experiences field, there is nothing for the SDK to personalize.
- Solution:
Log in to the Contentful web app, open the Contentful Personalization app configuration, and select the content types that should be personalizable. After saving, verify that your entries now have the nt_experiences field.
Include depth is too low
- Symptoms:
Entries have nt_experiences links in Contentful, but ExperienceMapper.isExperienceEntry() filters them all out. The component always renders the baseline.
- Cause:
Your Contentful API queries use an include depth that is too low. Experience entries and their variant references need at least two levels of resolution to be usable.
- Solution:
Increase the include parameter in your getEntries or getEntry calls to at least 3. Complex page structures with nested sections often use include: 10.
Provider and scope issues
Some pages personalize, others do not
- Symptoms:
Personalization works on certain pages but not others, even though the entries have experiences configured.
- Cause:
The NinetailedProvider is mounted too low in the component tree. Components outside the provider’s scope cannot access personalization.
- Solution:
Move the provider higher so that all personalizable components are within its scope. If you’re using Next.js, you can typically move it to pages/_app.tsx (Pages Router). If you’re using React, move it to the root layout wrapper (App Router).
Hydration mismatch warnings
- Symptoms:
Browser console shows React hydration warnings. Variants may flicker or render incorrectly on first load.
- Cause:
The server-rendered HTML and the client-side initial render produce different output. This often happens when the server renders a personalized variant but the client initializes with baseline data before the SDK loads.
- Solution:
Ensure the data contracts between server and client are consistent. In hybrid SSR setups, pass the experience selections to the client deterministically so the initial render matches.
Analytics and tracking issues
Double-counted page views
- Symptoms:
Analytics show approximately twice the expected number of page views.
- Cause:
This happens in two common scenarios:
- Pages Router with manual tracking: The Next.js Pages Router integration already tracks page changes automatically. Adding manual
page()calls creates duplicates. - Hybrid SSR without preflight: Both the server and the client SDK send page events. Without preflight mode, both events are persisted.
- Solution:
- For scenario 1: Remove any manual
page()calls for route changes. The integration handles this. - For scenario 2: Add
?type=preflightto the server-side API call. The server gets the same response but does not persist events — the client SDK handles persistence.
- For scenario 1: Remove any manual
Insights data is missing
- Symptoms:
Personalization works, but the Insights dashboards doesn’t display any data. Experiment results are empty.
- Cause:
Component view tracking requires the Insights plugin running in the browser. If you have a server-only setup with no client SDK, viewport-based tracking cannot work.
- Solution:
Ensure @ninetailed/experience.js-plugin-insights is installed and added to the provider’s plugin list. If you are running a server-only setup, understand that experiment measurement requires a client-side component. This is a fundamental limitation, not a configuration issue.
Wrong analytics plugin installed
- Symptoms:
You expected component insights and experiment measurement, but nothing appears.
- Cause:
The project installed @ninetailed/experience.js-plugin-analytics (the base class) instead of @ninetailed/experience.js-plugin-insights (the tracking implementation).
- Solution:
Replace the analytics plugin with the Insights plugin:
SSR and edge issues
Progressive timeout increases
- Symptoms:
API response times get slower over days or weeks. What started as sub-100ms responses now regularly times out.
- Cause:
The server-side code sends identify events on every page request. Each identify with a userId triggers a profile merge operation, which adds a redirect pointer. Over time, these redirect chains grow linearly, and each subsequent request must traverse a longer chain.
- Solution:
Only send identify events at login, signup, or account-linking moments, not on every request. Use a session-level flag (for example, a cookie) to track whether identification has already happened in the current session. After you fix the code, existing chains will self-heal over several days as the system shortcuts them during traversal.
Duplicate profiles for the same visitor
- Symptoms:
A visitor appears to have two separate profile IDs. Server-side and client-side show different profiles.
- Cause:
The ntaid cookie is being set from the request value instead of the API response. After profile merges or relocations, the canonical profile ID changes. If you set the cookie from the old request value, the client SDK finds a stale ID and creates a new profile.
- Solution:
Always set the ntaid cookie from response.data.profile.id, never from the incoming request cookie:
Geo-based audiences not matching on the server
- Symptoms:
Audiences based on visitor location work client-side but never match on server-rendered or edge-rendered requests.
-
Cause: The server-side page event does not include the visitor’s country code. Without
countryCodein the event context, the Experience API cannot evaluate geographic audiences. -
Solution:
Include the visitor’s country code from your edge platform’s geo headers:
Other common issues
Preview UI appears in production
- Symptoms:
The Personalization preview widget is visible to end users.
- Cause:
The Preview plugin is instantiated without an environment check.
- Solution:
Gate the Preview plugin behind an environment or preview mode check. Only instantiate it when you are in a development or preview context:
Flash of default content
- Symptoms:
The baseline content is briefly visible before the personalized variant appears.
- Cause:
This is expected behavior in client-side-only setups. The server sends static HTML with baseline content, and the SDK swaps it after hydration.
Important: This is not a bug. If the flash is unacceptable, switch to a hybrid SSR or edge setup where the server renders personalized HTML before it reaches the browser. See Edge and Server Side Rendering.