Skip to main content

Nuxt Verify and troubleshoot

Troubleshooting

Why is there no banner?

Check. DevTools shows the matched policy, and useConsentSnapshot() exposes policyRule.prompt. In the Network tab, find the init request.

Fix. Work through these causes in order:

  1. The visitor's policy asks for no banner. A policy with prompt none shows no banner, which is common in opt-out regions. See why the banner may be absent.
  2. ConsentRoot is inside a page that unmounts. Put it in app.vue.
  3. The policy did not resolve. In the default manifest() mode, /api/c15t/init must return 200. With hosted(), the backend's /init must. While resolution fails, c15t shows no UI and keeps every optional category denied.
  4. A stored choice exists. Clear the c15t cookie and localStorage entry, or use a private window.

The page throws IABUnavailableError

Check. Nuxt shows its error page, and the error has the code C15T_IAB_UNAVAILABLE and reads "c15t: this visitor's policy uses IAB TCF, but iab is not set." The visitor's policy uses the iab model and the backend sent its vendor list, but the module has no iab option, or has iab: false or iab: { enabled: false }. The standard banner does not handle the IAB model, so c15t throws rather than leave the visitor with no consent UI. A server render fails with status 500. A prerendered or ssr: false page shows the error page once the browser resolves the policy.

Fix. If the site uses IAB TCF, set c15t: { iab: {} } in nuxt.config.ts, or iab: { cmpId } when your backend sends no CMP ID. See IAB TCF. If it does not, remove the iab model from the policy for that region in your Inth project or policy pack. A backend that answers gvl: null turns IAB off for that request, and nothing throws.

The banner is not in the server HTML

Check. Only request-time rendering in manifest() or hosted() mode puts the banner in the HTML. manifest({ resolve: 'browser' }) and offline() resolve in the browser. Prerendered routes, routes with a prerender, swr, isr or cache rule, nuxt generate output and ssr: false send the same HTML to every visitor, so the banner appears after the browser resolves the policy.

Fix. If a route needs the banner in its HTML, render it at request time with manifest(), the default, or hosted().

Check. With server rendering, the render waits at most timeoutMs for the policy, 500 milliseconds by default. A slower backend renders the page without consent UI and the browser resolves the policy after hydration. Check the server log for backend errors.

Fix. Raise timeoutMs if your backend is reachable but slow.

The banner comes back after a visitor rejects

Check. Filter the Network tab by subjects and check that the POST to your backend returns 2xx. A CORS error means the site's origin is missing from your Inth project's trusted origins.

Fix. Add the site's origin to the trusted origins.

Check. Compare storageConfig.storageKey in the module options and app.config.ts. When they differ, the choice is written under one name and read under another.

Fix. Use the same storageKey in both places.

Check. Look for a cache in front of Nuxt, such as a CDN, that stores pages Nitro renders per request. The module only recognizes Nuxt's own prerendering and prerender, swr, isr or cache route rules.

Fix. Use a route rule for those pages, or have the CDN skip HTML. See prerendered and cached routes.

Check. Look for scripts in nuxt.config.ts. Module options drop functions.

Fix. Move scripts to app/app.config.ts.

Check. Look at the vendor ID. It is still a placeholder, such as phc_your_project_key.

Fix. Replace it with the ID from your vendor account.

Check. Look for a second loader, such as the vendor's own Nuxt module or a useHead script, that also loads the vendor.

Fix. Remove it, or the vendor loads without consent.

nuxt build or nuxt dev can't fetch the manifest

Check. The module downloads the manifest when nuxt build runs and when nuxt dev starts, and waits at most 10 seconds. When the download fails, nuxt build stops with an error that starts with could not fetch the consent manifest from <url> during the build. nuxt dev logs the same message as a warning, and the server fetches the policy at runtime. The part in parentheses names the cause:

  • fetch failed with a network error, or no response within 10 seconds: the build machine can't reach the backend.

  • /manifest responded 404 Not Found, or another status: the URL doesn't point at your project's backend, or the backend is down.

  • returned an invalid consent manifest: the URL answers, but not with a c15t manifest.

  • manifest() needs a backend URL: neither backendURL nor NUXT_PUBLIC_C15T_BACKEND_URL (or NUXT_PUBLIC_INTH_PROJECT_URL) is set. nuxt build and nuxt dev both stop, whatever onBuildError says, because the browser saves consent to the backend. manifest({ snapshot }) stops with manifest({ snapshot }) still needs a backend URL for the same reason.

With onBuildError: 'fail', this error also appears:

  • build-time manifests require an absolute upstream URL: backendURL or manifestURL is a relative path. Without onBuildError: 'fail', the module skips the fetch instead.

Fix. Set NUXT_PUBLIC_C15T_BACKEND_URL (or NUXT_PUBLIC_INTH_PROJECT_URL), or backendURL under the c15t key, to your project's absolute backend URL, including any path prefix; the module adds /manifest. To download from another address, set mode: manifest({ manifestURL }) to the full manifest URL, which is used as written. Make sure the build machine can reach the backend. To deploy while it is down, build with C15T_ON_BUILD_ERROR=runtime, or set onBuildError: 'runtime'. To never fetch at build time, set mode: manifest({ source: 'runtime' }). nuxt prepare doesn't download the manifest, so installing dependencies works offline.

Policy edits don't show up

Check. When the build bundles a manifest, the policy is fixed when you build. Changing NUXT_PUBLIC_C15T_BACKEND_URL on a running server moves consent saves, not the policy.

Fix. Build again after editing policies, translations or vendors. If your CI caches build output, skip the cache for that build. To pick up edits without rebuilding, set mode: manifest({ source: 'runtime' }).

Static hosting returns 404 for /api/c15t/init

Check. A static host has no Nuxt server routes, so /api/c15t/init and /api/c15t/manifest do not exist after deployment. nuxt generate warns about this when mode is the default manifest().

Fix. Set mode: manifest({ resolve: 'browser' }) with routePrefix: false, or mode: hosted(), as in deploy to static hosting. If the browser reports a CORS error for a consent save or /init, add your site's origin to the Inth project's trusted origins.

The module throws "c15t.mode in nuxt.config.ts is a transport"

Check. nuxt.config.ts imports manifest, hosted or offline from c15t/vue/vue-plugin. Those return browser transports, which cannot travel through runtime config.

Fix. Import them from c15t/vue, the module entry, which returns plain data.

Options from an earlier alpha have no effect

Check. The c15t key sets manifest, manifestURL, manifestSnapshot, buildManifest, geoURL, initRoute or manifestRoute. The module no longer reads them.

Fix. Use mode and routePrefix:

Earlier optionNow
manifest: 'server'Nothing: manifest() is the default
manifest: 'client'mode: manifest({ resolve: 'browser' })
manifest: falsemode: hosted()
manifestURL, geoURL, manifestSnapshotmanifest({ manifestURL }), manifest({ geoURL }), manifest({ snapshot })
buildManifest: falsemode: manifest({ source: 'runtime' })
buildManifest: trueonBuildError: 'fail'
initRoute, manifestRouteroutePrefix, one catch-all for both
NUXT_PUBLIC_C15T_MANIFEST_URL, NUXT_C15T_MANIFEST_URLmode: manifest({ manifestURL }) in nuxt.config.ts. The variables are no longer read, so the manifest URL is fixed at build time. NUXT_PUBLIC_C15T_BACKEND_URL and NUXT_C15T_BACKEND_URL still work.
domainNothing. Saves send the page's hostname.
ConsentPreferencesLinkConsentDialogLink. The floating ConsentDialogTrigger keeps its name.
ConsentFrameConsentGate

Every first visit calls /init on a static site

Check. With manifest({ resolve: 'browser' }) the browser has no request location. When your policy depends on the visitor's country or region, the browser asks the backend's /init for it on the first visit.

Fix. Pass geoURL to manifest() with a route on your site that returns the visitor's country and region, so the browser resolves the policy itself. Or use hosted(), which makes the same /init request without bundling the manifest.

A composable throws "Kernel not found"

Check. The composable ran outside the Nuxt app, for example in a server route or a plain module.

Fix. Call consent composables inside a component's setup.

Every visitor resolves the same location

Check. The server found no location header, or an x-c15t-country header that your own code adds, such as a test middleware, won. A header a visitor sends only changes that visitor's policy. Check which headers reach Nuxt.

Fix. Choose the headers you trust as geography headers describes.

More help

Troubleshoot consent covers problems shared by every framework, such as a missing banner, analytics that load before a choice, imports that fail because npm installed c15t v2, choices that disappear on reload, server HTML that differs from the browser, static builds that fail and content blockers that hide the consent UI.