Skip to main content

Troubleshooting

Diagnose setup, SSR, authentication, realtime, and upload failures from the boundary inward.

Start with the first boundary that fails. Avoid changing several options at once; that hides the cause.

Module cannot find Convex

Symptom: setup fails with a missing Convex URL, or queries never connect.

  1. Confirm NUXT_PUBLIC_CONVEX_URL exists in the environment running Nuxt.
  2. Confirm it points to the intended .convex.cloud deployment.
  3. Restart Nuxt after changing local environment files.
  4. If the value was added only after a production build, use Nuxt's NUXT_PUBLIC_* runtime override or rebuild.

SSR succeeds but hydration changes the page

Symptom: initial HTML and the hydrated client show different identity or query data.

  • Do not read browser-only state during server rendering.
  • Pass query arguments that are the same on the server and in the browser.
  • Keep the same query auth mode on server and client.
  • Use useConvexQuery instead of manually combining a server fetch and a separate client subscription.
  • Check the structured query status and error instead of treating empty data as loading.

Auth proxy fails

Symptom: sign-in returns a proxy or token-exchange error.

Check, in order:

  1. convex.siteUrl or NUXT_PUBLIC_CONVEX_SITE_URL targets the correct HTTP Actions origin.
  2. Better Auth routes are registered in convex/http.ts.
  3. Convex has SITE_URL set to the exact Nuxt origin.
  4. Convex has versioned BETTER_AUTH_SECRETS, and its deployment-owned built-in CONVEX_SITE_URL matches the selected deployment.
  5. Nuxt and Convex share the same private BCN_AUTH_PROXY_IP_SECRET.
  6. The deployment preserves the /api/auth/* Nitro route.
  7. No proxy or CDN rewrites the host, cookies, request bytes, or redirect behavior unexpectedly.

Auth fails with a trusted client IP header error

Symptom: the build fails with auth.trustedClientIpHeader is required outside exact loopback development origins, or every auth request fails with Trusted client IP header must contain exactly one valid IP address.

  1. Set BCN_AUTH_TRUSTED_CLIENT_IP_HEADER for the build as well as the runtime. nuxt.config.ts reads it during the build.
  2. Check that the header exists on real requests and contains one IP address, not a list. On Vercel, use x-real-ip; see Deploy to Vercel.
  3. Repeated 429 responses mean that many users share the same reported IP. Pick a header that contains the client IP, not the address of a proxy.

Social sign-in returns account_not_linked

A user with the same email address already exists. The library never joins accounts automatically. The user signs in with the existing method and links the provider with client.linkSocial(). See Social sign-in.

A protected query runs anonymously

auth: 'optional' waits until the auth state is known but permits anonymous execution. Use auth: 'required' when the call must not run without identity, and still enforce identity inside the Convex handler.

Use auth: 'none' only for intentionally public data. It always uses the dedicated anonymous transport, even while the user is signed in.

Realtime stops updating

  • Inspect connection state.
  • Check that CSP and the network permit the exact Convex WebSocket origin.
  • Confirm the component still owns the query; subscriptions stop after the last owner leaves.
  • Do not construct a separate raw ConvexClient for the same state.

Errors lose useful detail

Use ConvexCallError fields—kind, code, status, data, and functionName—rather than parsing message strings. functionName names the Convex function that failed. Run unknown failures, including $fetch errors from Nitro routes, through normalizeConvexError. The library intentionally does not expose raw upstream bodies or serialize cause.

A server error shows Convex application error when its ConvexError data has no string message. Throw new ConvexError({ code, message }) or new ConvexError('Readable text') to give the user a specific message.

A call fails with CLIENT_UNAVAILABLE

The browser Convex client does not exist yet. This happens during server rendering, in a build without a Convex URL, or before the browser runtime starts.

  • Call mutate, run, upload, useConvex() methods, and useConvexAuth().client methods from event handlers or onMounted(), not during setup.
  • Use useConvexQuery for server-rendered data and serverConvex(event) in Nitro handlers.
  • Confirm NUXT_PUBLIC_CONVEX_URL is set for the environment.

An SSR query times out or returns too much data

An SSR query or serverConvex call fails with a transport error when it runs longer than convex.server.queryTimeoutMs or returns more than convex.server.maxResponseBytes. Narrow or paginate the query first. Raise the server limits only for a measured need.

Uploads stall

Check file limits before requesting an upload URL, confirm the generated URL has not expired, and inspect the active upload's progress and error state. For an application-owned multi-file workflow, also inspect its worker scheduling and per-item state. A successful storage upload does not automatically create an application record; treat those as two explicit steps and define compensation for the second step failing.

For a minimal reproduction, disable auth only if the failure is unrelated to identity, reduce the page to one explicit query or call, and record the public structured error. Never attach session cookies, bearer tokens, or secrets to an issue.