# Troubleshooting the Next.js App Router SDK

> Find the cause of frequent problems with the Next.js App Router SDK, and use the debugging tools of the SDK and Uniform Context.

Source: https://docs.uniform.app/docs/sdk/nextjs-app-router/troubleshooting

Find your problem in the tables below. Each row gives the frequent causes and the fix. Then use the [debugging tools](#debugging-tools) to find the cause.

## Pages and routes

| Problem | Cause | Fix |
| --- | --- | --- |
| The page gives a 404. | The composition is not published, or the project map node has no composition. | Publish the composition. Attach it to the node of the path. |
| The page gives a 404, but the composition is published. | The path in the browser is different from the project map path, for example `/en/about` and `/en-US/about`. | Change the path in `rewriteRequestPath` of the middleware. Refer to [Middleware configuration](https://docs.uniform.app/docs/sdk/nextjs-app-router/middleware). |
| A page in an ordinary Next.js route gives a 404. | No Next.js route matches the path of the project map node. | Add a route for each project map node that has a composition. |
| The page shows "Component not found: hero". | `resolveComponent` has no entry for the component type, or the public ID is different. | Add the type to `resolveComponent`. Use the public ID from the component library. |
| A slot or a region is empty. | The slot name in your code is different from the public ID of the slot. | Use the public ID of the slot, for example `slots.content`. |
| A component with visibility rules is not in the server HTML. | This is expected. Visibility rules run only in the browser. | Do not put content for search engines in a component with visibility rules. |
| A redirect gives 308, not 301. | Next.js sends 308 for `permanentRedirect()` and 307 for `redirect()`. | No fix. Next.js does not keep the exact status code. |
| The route builds as dynamic (`ƒ`), not static (`●`). | The page reads `searchParams`, `cookies()` or `headers()` on published requests. | Read them only in draft mode. The middleware writes the request state into the code. |
| The build fails with an error about an empty `generateStaticParams`. | Cache Components is on, and `generateStaticParams` returns `[]`. | Return at least one code, for example `createUniformStaticParams({ paths: ["/"] })`. |

## Preview and Canvas

| Problem | Cause | Fix |
| --- | --- | --- |
| The preview gives `401 No preview secret is configured`. | `UNIFORM_PREVIEW_SECRET` is not set in the app. | Set it, and start the server again. |
| The preview gives `401 Invalid preview secret`. | The `secret` in the preview URL is different from `UNIFORM_PREVIEW_SECRET`. | Use the same value in the two places. |
| The preview gives a 404. | Canvas sends the project map path, and the app has no route for it. For example, Canvas sends `/en-US/about`, and the site uses `/en/about`. | Change the path in `resolveFullPath` of the preview handler. Refer to [Preview](https://docs.uniform.app/docs/sdk/nextjs-app-router/preview#change-the-preview-path). |
| The pattern preview gives a 404. | The playground route is not at `playgroundPath`, or `processPlaygroundPath` changes the path. | Put the playground page at `${playgroundPath}/[code]`. Do not change the path with `processPlaygroundPath`. |
| The pattern preview shows "Not Found: page". | The pattern is a composition pattern, and its root type is not in `resolveComponent`. This occurs with hybrid pages. | Render page-type patterns with your region wrapper. Refer to [Hybrid pages](https://docs.uniform.app/docs/sdk/nextjs-app-router/recipes/hybrid-pages). |
| The preview shows published content. | The middleware is `proxy.ts`, or it is not on the edge runtime. On Vercel, the SDK cannot read draft mode there. | Use `middleware.ts` with `runtime: "experimental-edge"`. |
| Canvas cannot select the components. | The URL does not have `is_incontext_editing_mode=true`, so the page is in the draft state, not the editor state. | Open the composition from Canvas. Canvas adds the query string. |
| Canvas selects the wrong component, or the markers occur two times. | The page renders the same composition more than one time. | Render each composition one time on a page. Use one `UniformContext` for the page. |
| The text edit in Canvas does nothing. | The component renders the parameter value directly, not with `UniformText`. | Render text parameters with `UniformText`. |

## Personalization and edge mode

| Problem | Cause | Fix |
| --- | --- | --- |
| The visitor sees the default variant first, and then a different variant. | This is lite mode. The browser chooses the variants after hydration. | Use edge mode. Refer to [Edge mode execution](https://docs.uniform.app/docs/sdk/nextjs-app-router/edge-mode). |
| Edge mode shows the default variant for a new signal. | The manifest of the edge does not have the new signal yet. | Publish the manifest. On Vercel, the `manifest.published` webhook must reach the preview route with `onRevalidateTags: expireVercelRuntimeCacheTags`. With a manifest in the build, build again. |
| Edge mode does not work under `next dev`. | Edge mode needs published pages from the cache. | Use a production build: `npm run build && npm run start`. |
| Edge mode does not work in the Canvas preview. | This is expected. Draft requests do not use edge mode. | No fix. The browser chooses the variants in preview. |
| A page under a dynamic project map node stays without edge processing. | The edge route store recorded the page without placements, and the webhook expires only the static part of the path. | Set a `ttl` in `vercelEdgeRouteStore({ ttl })`, or use a filter that does not skip the page. |
| A new signal or test does not work under `next dev`. | `UniformContext` gets the manifest with `force-cache`, also in development. | Do a hard reload, or delete the `.next` folder. |
| `curl` gets all variants, or the default variant. | The request is not a document request for the middleware. | Send `Accept: text/html`, or no `Accept` header. |

## Content updates and webhooks

| Problem | Cause | Fix |
| --- | --- | --- |
| A published change does not show on the site. | No webhook is configured, or it does not reach the app. | Add a webhook for `/api/preview`. Refer to [Caching](https://docs.uniform.app/docs/sdk/nextjs-app-router/caching#webhooks). |
| The first request after a publish shows the old content. | This is expected. Next.js serves the old page and renders the new page in the background. | Load the page again. |
| The webhook answers `401`. | The `secret` query string or the Svix signature is not correct. | Use the same value as `UNIFORM_PREVIEW_SECRET`. Use the signing secret of the webhook in `UNIFORM_WEBHOOK_SECRET`. |
| The webhook answers `{ "handled": false }`. | The handler does not use this event. | Select only the events in [Configure the webhook](https://docs.uniform.app/docs/sdk/nextjs-app-router/caching#configure-the-webhook). |
| `revalidatePath("/about")` does nothing. | The middleware rewrites `/about` to `/uniform/[code]`. With a rewrite, `revalidatePath` must get the destination route. | Use `revalidateTag("path:/about", "max")`. |

## Install, build and types

| Problem | Cause | Fix |
| --- | --- | --- |
| npm shows `ERESOLVE` for a `@uniformdev` package. | A peer dependency range does not include the developer preview version. | Add `"overrides": { "@uniformdev/context": "$@uniformdev/context" }` to `package.json`. |
| The browser Context does not work, or hooks return `undefined`. | Two versions of `@uniformdev/context` are installed. | Use the same version for all `@uniformdev` packages. Run `npm ls @uniformdev/context` to find the copies. |
| Next.js shows a warning that `middleware.ts` is deprecated, or that the edge runtime is deprecated. | This is expected. | Keep `middleware.ts` with `runtime: "experimental-edge"`. |
| TypeScript shows an error for `parameter={title}` on `UniformText`. | The parameter is optional, but the `parameter` prop is required. | Render the component only when the parameter exists: `{title ? <UniformText … /> : null}`. |

---

## Debugging tools

### Read the code of a request

The middleware rewrites each request to `/uniform/[code]`. To see the values in the code, add a log to the composition route in development:

`app/uniform/[code]/page.tsx`

```tsx
import { deserializePageState, UniformComposition, type UniformPageParameters } from "@uniformdev/next-app-router";
import { resolveComponent } from "@/components/resolveComponent";

export default async function UniformPage(props: UniformPageParameters) {
  const { code } = await props.params;
  if (process.env.NODE_ENV === "development") {
    console.log(deserializePageState({ code }));
  }
  return <UniformComposition code={code} resolveComponent={resolveComponent} />;
}
```

The log shows the `PageState`:

| Field | Look for |
| --- | --- |
| `routePath` | The path that the Route API gets. Is it the project map path? |
| `compositionState` | `64` published, `0` draft, `63` editor. Is draft mode on? |
| `edgeMode` | `true` when the edge processes the page. |
| `defaultConsent`, `locale`, `releaseId`, `keys` | The values from the middleware options. |

### Look at the response headers

```bash
curl -sI http://localhost:3000/about
```

| Header | Meaning |
| --- | --- |
| `cache-control: private, no-store` | The edge processed the page, and sent it with the visitor state and without an ETag. |
| `cache-control: private, no-cache` with `etag` | The edge processed the page. The browser can get `304` next time. |
| `x-nextjs-cache: HIT`, `STALE` or `MISS` | The page came from the Next.js cache, from an old cache entry, or from a new render. |

To see the cached page without the edge, send the origin header:

```bash
curl -sI -H "x-uniform-edge-origin: 1" http://localhost:3000/uniform/<code>
```

### Look at the visitor state from the edge

In edge mode, the middleware writes the visitor state into the `__UNIFORM_DATA__` script:

```bash
curl -s "http://localhost:3000/?utm_campaign=launch" | grep -o '__UNIFORM_DATA__[^<]*'
```

The script contains the scores (`ssv`), the tests and the personalization variants of the visitor. It is empty when the response has an ETag.

### Use the Uniform Context DevTools

The default client context turns on the Uniform Context DevTools, unless `context.disableDevTools` is `true` in the server configuration. With the DevTools, you can see and change the scores, quirks and tests of the visitor in the browser. When you change them, the page refreshes. Refer to [Context DevTools](https://docs.uniform.app/docs/guides/classification/context-devtools).

### Look at the cookies

| Cookie | Meaning |
| --- | --- |
| `__prerender_bypass` | Next.js draft mode is on. The preview handler sets it. |
| `ufvd` | The visitor data of the Uniform Context. |
| `ufqc` | Quirks from the middleware. It expires after 10 seconds. |

To turn off draft mode, open `/api/preview?disable=true&path=/`.

### Look at the webhook response

The webhook handler answers with the tags and paths that it revalidated:

```json
{ "handled": true, "tags": ["composition:6c4f…", "path:/about"], "paths": ["/about"] }
```
