---
title: "Instant Validation"
description: "Instant Validation provides development and build-time verification for React Server Component routes opting into the instant segment configuration, ensuring that prefetched shells and dynamic rend..."
last_updated: "2026-10-12T06:38:56.887958+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-rendering/instant-validation"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx)
- [packages/next/src/server/app-render/dynamic-rendering.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/dynamic-rendering.ts)
- [packages/next/src/build/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/index.ts)
- [packages/next/src/server/app-render/instant-validation/instant-config.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx)
- [packages/next/src/server/app-render/instant-validation/instant-validation-error.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation-error.ts)
- [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx)
- [packages/next/src/server/dev/dev-validation-worker.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker.ts)
- [packages/next/src/server/app-render/instant-validation/instant-samples.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-samples.ts)
- [packages/next/src/server/app-render/instant-validation/boundary-constants.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-constants.ts)
- [packages/next/src/client/components/instant-samples.browser.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-samples.browser.ts)
- [packages/next/src/client/components/instant-samples.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-samples.ts)
- [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/route-modules/app-route/module.ts)
- [packages/next/src/server/request/draft-mode.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/request/draft-mode.ts)
- [packages/next/src/next-devtools/dev-overlay/container/errors.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/container/errors.tsx)
- [packages/next/src/client/components/instant-validation/impl.browser.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-validation/impl.browser.tsx)
- [packages/next/src/build/webpack/plugins/next-types-plugin/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts)
- [packages/next/src/server/app-render/dev-validation-worker-snapshot.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/dev-validation-worker-snapshot.ts)
- [packages/next/src/build/utils.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/utils.ts)
- [packages/next/src/server/app-render/instant-validation/boundary-tracking.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-tracking.tsx)
- [packages/next/src/client/components/instant-validation/boundary.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-validation/boundary.tsx)
- [packages/next/src/client/components/instant-validation/impl.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-validation/impl.tsx)
- [packages/next/src/build/segment-config/app/app-segment-config.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/segment-config/app/app-segment-config.ts)
</details>

## Overview

Instant Validation provides development and build-time verification for React Server Component routes opting into the `instant` segment configuration, ensuring that prefetched shells and dynamic render paths fulfill sample requirements and boundary constraints. By intercepting route parameters, cookies, and headers, and executing validation passes through dedicated workers, it detects missing samples, unrendered segments, and illegal dynamic access before production deployment.

Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:1-53](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L1-L53), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:1-48](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L1-L48)

## Segment Configuration and Validation Triggers

### Config Resolution and Traversal

`[[Instant Validation]]` resolves instant validation eligibility by walking loader trees and inspecting segment-level `instant` and `prefetch` options. The validation eligibility depends on whether a segment is a page or default segment, whether an explicit `instant` export or `prefetch: 'partial'` is configured, and whether `unstable_disableValidation`, `unstable_disableDevValidation`, or `unstable_disableBuildValidation` are set.

```tsx
async function visit(tree: LoaderTree): Promise<void> {
  if (disabled) return

  const { mod: layoutOrPageMod } = await getLayoutOrPageModule(tree)
  const instantConfig = layoutOrPageMod
    ? (layoutOrPageMod as AppSegmentConfig).instant
    : undefined

  if (instantConfig === false) {
    // Explicit opt-out. Doesn't itself trigger validation.
  } else if (instantConfig === true) {
    // Explicit opt-in using the default level.
    if (level <= baseValidationLevel) {
      needsValidation = true
    }
  } else if (typeof instantConfig === 'object' && instantConfig !== null) {
    if (
      instantConfig.unstable_disableValidation === true ||
      (level === VALIDATION_LEVEL.WARNING &&
        instantConfig.unstable_disableDevValidation === true) ||
      (level === VALIDATION_LEVEL.ERROR &&
        instantConfig.unstable_disableBuildValidation === true)
    ) {
      disabled = true
      return
    }

    if (instantConfig.level !== undefined) {
      const configuredLevel =
        instantConfig.level === 'experimental-error'
          ? VALIDATION_LEVEL.ERROR
          : VALIDATION_LEVEL.WARNING
      if (level <= configuredLevel) {
        needsValidation = true
      }
    } else if (level <= baseValidationLevel) {
      needsValidation = true
    }
  } else if (applyDefaultValidation && isImplicitValidationSegment(tree[0])) {
    // No explicit config. Implicit validation applies to page/default
    // segments when the default level is active for this mode.
    needsValidation = true
  }

  const { parallelRoutes } = parseLoaderTree(tree)
  for (const parallelRouteKey in parallelRoutes) {
    await visit(parallelRoutes[parallelRouteKey])
    if (disabled) return
  }
}
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:169-218](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L169-L218)

### Validation Triggers and Modes

The system evaluates validation checks under two distinct execution targets: development mode via `anySegmentNeedsInstantValidationInDev` and build mode via `anySegmentNeedsInstantValidationInBuild`. These map directly to `VALIDATION_LEVEL.WARNING` and `VALIDATION_LEVEL.ERROR` respectively, governed by the active `workStore.validationLevel`.

| Validation Level Setting | Validation Level Enum | Manual Flag | Description |
| :--- | :--- | :--- | :--- |
| `manual-warning` | `VALIDATION_LEVEL.WARNING` | `true` | Manual warning mode (disables implicit validation) |
| `warning` | `VALIDATION_LEVEL.WARNING` | `false` | Default warning mode for development |
| `experimental-manual-error` | `VALIDATION_LEVEL.ERROR` | `true` | Manual error mode (disables implicit validation) |
| `experimental-error` | `VALIDATION_LEVEL.ERROR` | `false` | Default error mode for production builds |

```tsx
enum VALIDATION_LEVEL {
  WARNING = 0,
  ERROR = 1,
}

/**
 * Walks the loader tree and checks if any segment has an `instant` config
 * that needs validating for the given mode.
 *
 * - Explicit `instant` exports are checked against mode.
 * - Page and default segments without an explicit config get implicit
 *   validation when the default validation level applies to this mode.
 * - `unstable_disableValidation` on any segment kills validation for
 *   the whole tree.
 */
async function anySegmentNeedsInstantValidation(
  rootTree: LoaderTree,
  level: VALIDATION_LEVEL
): Promise<boolean> {
  const workStore = workAsyncStorage.getStore()
  if (!workStore) {
    throw new InvariantError(
      'anySegmentNeedsInstantValidation must run inside a WorkStore'
    )
  }
  const { validationLevel } = workStore

  let baseValidationLevel: number = VALIDATION_LEVEL.WARNING
  let manualValidation: boolean = false
  switch (validationLevel) {
    case 'manual-warning':
      manualValidation = true
    // intentional fallthrough
    case 'warning':
      baseValidationLevel = VALIDATION_LEVEL.WARNING
      break
    case 'experimental-manual-error':
      manualValidation = true
    // intentional fallthrough
    case 'experimental-error':
      baseValidationLevel = VALIDATION_LEVEL.ERROR
      break
    default:
      validationLevel satisfies never
  }
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:112-156](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L112-L156)

> [!NOTE]
> Framework-synthesized error and not-found routes (`UNDERSCORE_GLOBAL_ERROR_ROUTE` and `UNDERSCORE_NOT_FOUND_ROUTE`) are excluded from implicit validation under non-manual default levels, ensuring that fallback error UI does not trigger unexpected validation failures unless explicitly opted in by the user.

Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:33-47](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L33-L47), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:158-165](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L158-L165)

### Segment Eligibility Rules

Implicit validation applies specifically to page and default segment keys (`PAGE_SEGMENT_KEY` or `DEFAULT_SEGMENT_KEY`) when no explicit `instant` config is present. Layout segments do not validate on their own; instead, segment sample resolution iterates through children slots to collect `unstable_samples` without merging outer and inner arrays.

```tsx
export function isImplicitValidationSegment(segment: Segment): boolean {
  const key = typeof segment === 'string' ? segment : segment[0]
  return key === PAGE_SEGMENT_KEY || key === DEFAULT_SEGMENT_KEY
}
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:28-31](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L28-L31), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:237-272](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L237-L272)

> [!WARNING]
> Setting `unstable_disableValidation: true` on any segment within a loader tree immediately short-circuits the entire tree traversal (`disabled = true`), disabling validation globally for that render request regardless of other configurations.

Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:185-194](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L185-L194)

## Boundary Placement and Marker Hierarchy

### Context and Boundary Insertion

During the traversal of route segment trees on the server, validation boundaries and slot markers are injected to isolate portions of the React component tree and attribute errors accurately. The injection mechanism relies on `InstantValidationBoundaryContext`, which accepts a `BoundaryPlacement` value of either `null` or a string `boundaryId`. When `PlaceValidationBoundaryBelowThisLevel` is invoked, it wraps its children in this context, signaling outer layout routers to render a validation boundary at that specific depth.

```tsx
type BoundaryPlacement =
  | null // do not place here
  | string // boundaryId -- place here

export const InstantValidationBoundaryContext =
  createContext<BoundaryPlacement>(null)

export function PlaceValidationBoundaryBelowThisLevel({
  id,
  children,
}: {
  id: string
  children: ReactNode
}) {
  return (
    // OuterLayoutRouter will see this and render a `RenderValidationBoundaryAtThisLevel`.
    <InstantValidationBoundaryContext value={id}>
      {children}
    </InstantValidationBoundaryContext>
  )
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:62-82](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L62-L82)

When `RenderValidationBoundaryAtThisLevel` consumes the boundary ID from the context, it instantiates an `InstantValidationBoundary` component and immediately resets the inner context value to `null`. This prevents child segments from redundantly rendering nested validation boundaries.

```tsx
export function RenderValidationBoundaryAtThisLevel({
  id,
  children,
}: {
  id: string
  children: ReactNode
}) {
  // We got a boundaryId from the context. Clear the context so that the children don't render another boundary.
  return (
    <InstantValidationBoundary id={id}>
      <InstantValidationBoundaryContext value={null}>
        {children}
      </InstantValidationBoundaryContext>
    </InstantValidationBoundary>
  )
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:84-99](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L99)

> [!CAUTION]
> Instant validation boundary components are strictly prohibited from appearing in browser bundles. If loaded in a browser environment (`typeof window !== 'undefined'`), the implementation throws an immediate `InvariantError`.

Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:13-17](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L13-L17)

### Tracking and Slot Attribution

The inner boundary component is backed by `NameSpace` keyed via `INSTANT_VALIDATION_BOUNDARY_NAME`. When rendered, it retrieves the current validation tracking state from `workUnitAsyncStorage` and registers the rendered boundary ID into the active tracking set.

```tsx
const NameSpace = {
  [INSTANT_VALIDATION_BOUNDARY_NAME]: function ({
    id,
    children,
  }: {
    id: string
    children: ReactNode
  }) {
    // Track which boundaries we actually managed to render.
    const state = getValidationBoundaryTracking()
    if (state === null) {
      throw new InvariantError('Missing boundary tracking state')
    }
    state.renderedIds.add(id)

    return children
  },
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:43-60](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L43-L60)

| Constant Name | Value / Format | Purpose |
| :--- | :--- | :--- |
| `INSTANT_VALIDATION_BOUNDARY_NAME` | `__next_instant_validation_boundary__` | Base name for the validation boundary component namespace |
| `INSTANT_SLOT_MARKER_PREFIX` | `__next_instant_slot_` | Prefix string for dynamic slot marker components |
| `INSTANT_SLOT_MARKER_SUFFIX` | `__` | Suffix string appended to dynamic slot marker components |

```typescript
export const INSTANT_VALIDATION_BOUNDARY_NAME =
  '__next_instant_validation_boundary__'

export const INSTANT_SLOT_MARKER_PREFIX = '__next_instant_slot_'
export const INSTANT_SLOT_MARKER_SUFFIX = '__'
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-constants.ts:1-5](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-constants.ts#L1-L5)

To attribute validation errors correctly when a boundary spans multiple parallel slots, `SlotMarker` dynamically caches and instantiates inner wrapper components using a `slotMarkerCache` map. This ensures that the slot index appears explicitly within the server-rendered component stack under names conforming to the slot marker prefix and suffix conventions.

```tsx
const slotMarkerCache = new Map<
  string,
  (props: { children: ReactNode }) => ReactNode
>()

export function SlotMarker({
  name,
  children,
}: {
  name: string
  children: ReactNode
}) {
  let Marker = slotMarkerCache.get(name)
  if (!Marker) {
    const ns = {
      [name]: function ({ children: c }: { children: ReactNode }) {
        return c
      },
    }
    Marker = ns[name]
    slotMarkerCache.set(name, Marker)
  }
  return <Marker>{children}</Marker>
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:114-137](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L114-L137)

## Dynamic Hole and Boundary Tracking

### Boundary Tracking State and Verification

State management for boundary validation relies on the `ValidationBoundaryTracking` type, which maintains a map of required boundaries and a set of successfully rendered boundary identifiers. The `requiredIds` map associates each boundary identifier (corresponding to a segment path) with an array of file paths for modules located inside that boundary's subtree. When a boundary spans multiple parallel slots, each slot contributes its own first-found module path so that all unrendered segments can be reported together.

```tsx
export type ValidationBoundaryTracking = {
  /**
   * Map from boundary id (the SegmentPath where a validation boundary is
   * placed) to the file paths of modules inside that boundary's subtree.
   * When the boundary spans multiple parallel slots, each slot contributes
   * its own first-found module path so all unrendered segments can be
   * reported together.
   */
  requiredIds: Map<string, string[]>
  renderedIds: Set<string>
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-tracking.tsx:1-11](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-tracking.tsx#L1-L11)

New tracking instances are initialized via `createValidationBoundaryTracking`, which optionally accepts a previous tracking state to copy required boundaries into a new `Map` while starting with a fresh `Set` for rendered identifiers. Verification that all required boundaries have been rendered is performed by `allRequiredBoundariesRendered`, which iterates over all keys in `requiredIds` and checks that every key exists within `renderedIds`.

```tsx
export function createValidationBoundaryTracking(
  /** Pass if the render is expected to render the same IDs as a previous one. */
  matchPrevious?: ValidationBoundaryTracking
): ValidationBoundaryTracking {
  return {
    requiredIds: matchPrevious ? new Map(matchPrevious.requiredIds) : new Map(),
    renderedIds: new Set(),
  }
}

export function allRequiredBoundariesRendered(
  state: ValidationBoundaryTracking
): boolean {
  for (const id of state.requiredIds.keys()) {
    if (!state.renderedIds.has(id)) {
      return false
    }
  }
  return true
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-tracking.tsx:13-32](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-tracking.tsx#L13-L32)

### Work Store Integration and Boundary Resolution

During server rendering, active boundary tracking state is retrieved through `workUnitAsyncStorage`. The helper function `getValidationBoundaryTracking` accesses the current asynchronous work store and inspects its type. If the store type is `'validation-client'`, it returns the associated `boundaryState`; for all other store types, it safely yields `null`.

```tsx
function getValidationBoundaryTracking(): ValidationBoundaryTracking | null {
  const store = workUnitAsyncStorage.getStore()
  if (!store) return null
  switch (store.type) {
    case 'validation-client':
      return store.boundaryState
    case 'prerender':
    case 'prerender-client':
    case 'prerender-legacy':
    case 'prerender-runtime':
    case 'request':
    case 'cache':
    case 'private-cache':
    case 'unstable-cache':
    case 'build-time-generator':
      break
    default:
      store satisfies never
  }
  return null
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:19-39](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L19-L39)

> [!WARNING]
> If `getValidationBoundaryTracking` encounters a store type that is unhandled by the switch statement, TypeScript's `store satisfies never` check enforces exhaustive type coverage at compile time, preventing silent validation failures during async store transitions.

Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:32-37](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L32-L37)

When an `InstantValidationBoundary` component mounts during tree construction, it invokes `getValidationBoundaryTracking` to obtain the active tracking store. If the store yields `null`, an `InvariantError` is immediately thrown indicating missing boundary tracking state. Otherwise, the boundary's `id` is added to `state.renderedIds`, recording successful render traversal for that segment path.

```tsx
    // Track which boundaries we actually managed to render.
    const state = getValidationBoundaryTracking()
    if (state === null) {
      throw new InvariantError('Missing boundary tracking state')
    }
    state.renderedIds.add(id)

    return children
  },
}
```
Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:51-60](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L51-L60)

## Sample Proxies and Parameter Instrumentation

### Cookie, Header, and Parameter Proxies

Exhaustive sample validation intercepts runtime data access by wrapping underlying request primitives in JavaScript proxies. The `createCookiesFromSample` function builds a `ReadonlyRequestCookies` wrapper from defined cookie sample configurations. It validates that any accessed cookie name exists within the declared sample set. If an undeclared cookie is queried via methods like `has` or `get`, the proxy invokes `trackMissingSampleErrorAndThrow` with an `InstantValidationError`.

```typescript
export function createCookiesFromSample(
  sampleCookies: InstantSample['cookies'],
  route: string
): ReadonlyRequestCookies {
  const declaredNames = new Set<string>()

  const cookies = new RequestCookies(new Headers())
  if (sampleCookies) {
    for (const cookie of sampleCookies) {
      declaredNames.add(cookie.name)
      if (cookie.value !== null) {
        cookies.set(cookie.name, cookie.value)
      }
    }
  }

  const sealed = RequestCookiesAdapter.seal(cookies)

  return new Proxy(sealed, {
    get(target, prop, receiver) {
      if (prop === 'has') {
        const originalMethod = Reflect.get(target, prop, receiver)
        const wrappedMethod: typeof originalMethod = function (name) {
          if (!declaredNames.has(name)) {
            trackMissingSampleErrorAndThrow(
              createMissingCookieSampleError(route, name)
            )
          }
          return originalMethod.call(target, name)
        }
        return wrappedMethod
      }
      if (prop === 'get') {
        const originalMethod = Reflect.get(target, prop, receiver)
        const wrappedMethod: typeof originalMethod = function (nameOrCookie) {
          let name: string
          if (typeof nameOrCookie === 'string') {
            name = nameOrCookie
          } else if (
            nameOrCookie &&
            typeof nameOrCookie === 'object' &&
            typeof nameOrCookie.name === 'string'
          ) {
            name = nameOrCookie.name
          } else {
            // This is an invalid input. Pass it through to the original method so it can error.
            return originalMethod.call(target, nameOrCookie)
          }

          if (!declaredNames.has(name)) {
            trackMissingSampleErrorAndThrow(
              createMissingCookieSampleError(route, name)
            )
          }
          return originalMethod.call(target, name)
        }
        return wrappedMethod
      }

      // TODO(instant-validation-build): what should getAll do?
      // Maybe we should only allow it if there's an array (possibly empty?)

      return Reflect.get(target, prop, receiver)
    },
  })
}
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:80-145](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L80-L145)

> [!CAUTION]
> Defining cookies via a manual `"cookie"` header inside `unstable_samples` is explicitly rejected. Attempting to pass a `"cookie"` header throws an `InstantValidationError`, requiring the use of the structured `cookies` array instead.

Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:171-176](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L171-L176)

### Client Validation Instruments

Client-side parameter and search parameter access is instrumented through wrapper utilities that inspect the active work unit store. When the store type is `'validation-client'`, functions such as `instrumentParamsForClientValidation` and `instrumentSearchParamsForClientValidation` extract declared keys from `validationSamples` and wrap the underlying parameters in exhaustive proxies.

```typescript
export function instrumentParamsForClientValidation<TPArams extends Params>(
  underlyingParams: TPArams
): TPArams {
  const workStore = workAsyncStorage.getStore()
  const workUnitStore = workUnitAsyncStorage.getStore()
  if (workStore && workUnitStore) {
    switch (workUnitStore.type) {
      case 'validation-client': {
        if (workUnitStore.validationSamples) {
          const declaredKeys = new Set(
            Object.keys(workUnitStore.validationSamples.params ?? {})
          )
          return createExhaustiveParamsProxy(
            underlyingParams,
            declaredKeys,
            workStore.route
          )
        }
        break
      }
      case 'prerender-runtime':
      case 'prerender-client':
      case 'prerender-legacy':
      case 'prerender':
      case 'cache':
      case 'request':
      case 'private-cache':
      case 'unstable-cache':
      case 'build-time-generator':
        break
      default:
        workUnitStore satisfies never
    }
  }
  return underlyingParams
}
```
Sources: [packages/next/src/client/components/instant-samples.ts:12-47](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-samples.ts#L12-L47)

| Instrument Function | Underlying Type | Target Store Type | Purpose |
| :--- | :--- | :--- | :--- |
| `instrumentParamsForClientValidation` | `TPArams` | `validation-client` | Wraps route parameters in an exhaustive validation proxy |
| `expectCompleteParamsInClientValidation` | `string` (expression) | `validation-client` | Asserts that all fallback route parameters are present |
| `instrumentSearchParamsForClientValidation` | `ReadonlyURLSearchParams` | `validation-client` | Wraps search parameters in an exhaustive validation proxy |

Sources: [packages/next/src/client/components/instant-samples.ts:12-122](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-samples.ts#L12-L122)

> [!NOTE]
> When client validation is executed outside of the development browser environment, `instant-samples.browser.ts` exports stub bindings set to `undefined`, disabling parameter instrumentation during standard browser bundle execution.

Sources: [packages/next/src/client/components/instant-samples.browser.ts:1-3](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/client/components/instant-samples.browser.ts#L1-L3)

## Staged Stream Generation and Transport

### Staged Stream Splitting and Transport Trees

### Staged Segment Data Collection

`collectStagedSegmentData` splits a unified prefetched stream into separate staged data chunks for individual route segments. The function determines which rendering stages to collect based on the provided `ValidationPrefetchKind` parameter. For a static app shell prefetch, it captures shells, prefetch APIs, and static prefetch stages; for static pages, it captures static prefetches; and for dynamic pages, it includes dynamic render stages.

```tsx
export async function collectStagedSegmentData(
  prefetchKind: ValidationPrefetchKind,
  ComponentMod: FlightComponentMod,
  renderFlightStream: RenderToFlightStream,
  fullPageChunks: StageChunks,
  fullPageDebugChunks: Uint8Array[] | null,
  startTime: number,
  stageEndTimes: StageEndTimes,
  clientReferenceManifest: ClientReferenceManifest,
  createDebugChannel: () => DebugChannelPair | undefined
): Promise<{ cache: SegmentCache; payload: InitialRSCPayload }> {
  const cache = createSegmentCache()

  let partialStages: SegmentStage[]
  switch (prefetchKind) {
    case ValidationPrefetchKind.StaticAppShell: {
      partialStages = [
        RenderStage.ShellStatic,
        RenderStage.PrefetchStatic_prefetchApi,
        RenderStage.PrefetchStatic,
        RenderStage.NavigationStatic, // TODO(cache-stages): only if needed
        RenderStage.PrefetchRuntime,
      ]
      break
    }
    case ValidationPrefetchKind.RuntimeAppShell: {
      partialStages = [
        RenderStage.ShellRuntime,
        RenderStage.PrefetchRuntime_prefetchApi,
        RenderStage.PrefetchRuntime,
        RenderStage.NavigationRuntime, // TODO(cache-stages): only if needed
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:189-219](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L189-L219)

| Validation Prefetch Kind | Captured Render Stages |
| :--- | :--- |
| `ValidationPrefetchKind.StaticAppShell` | `RenderStage.ShellStatic`, `RenderStage.PrefetchStatic_prefetchApi`, `RenderStage.PrefetchStatic` |
| `ValidationPrefetchKind.StaticPage` | `RenderStage.PrefetchStatic` |
| `ValidationPrefetchKind.DynamicPage` | `RenderStage.Dynamic` |

Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:202-218](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L202-L218)

> [!NOTE]
> The collected chunks are stored within a segmented cache structure mapped by `SegmentPath` keys, allowing the validation engine to inspect individual RSC payloads and flight data streams per route segment depth.

Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:200-201](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L200-L201)

### Route Segment Path Discovery

The validation pipeline traverses root seed data segments by parsing the initial RSC payload's transport tree nodes via `traverseRootSeedDataSegments`. Each transport node's segment is stringified into a hierarchical `SegmentPath`, combining parent paths with parallel route keys and encoded segment identifiers.

```tsx
function traverseRootSeedDataSegments(
  initialRSCPayload: InitialRSCPayload,
  processSegment: (segmentPath: SegmentPath, data: TransportSegmentData) => void
) {
  const rootNode = initialRSCPayload.t.t
  const rootPath = stringifySegment(transportSegmentToSegment(rootNode.s))
  return traverseTransportNodeSegments(rootPath, rootNode, processSegment)
}

function traverseTransportNodeSegments(
  path: SegmentPath,
  node: FullTransportNode,
  processSegment: (segmentPath: SegmentPath, data: TransportSegmentData) => void
): void {
  processSegment(path, node.d)

  const children = node.c
  if (children === undefined) {
    return
  }
  for (const [parallelRouteKey, childNode] of children) {
    const childPath = createChildSegmentPath(
      path,
      parallelRouteKey,
      transportSegmentToSegment(childNode.s)
    )

    traverseTransportNodeSegments(childPath, childNode, processSegment)
  }
}

function createChildSegmentPath(
  parentPath: SegmentPath,
  parallelRouteKey: string,
  segment: Segment
): SegmentPath {
  const parallelRoutePrefix =
    parallelRouteKey === 'children'
      ? ''
      : `@${encodeURIComponent(parallelRouteKey)}/`
  return `${parentPath}/${parallelRoutePrefix}${stringifySegment(segment)}` as SegmentPath
}

function stringifySegment(segment: Segment): SegmentPath {
  return (
    typeof segment === 'string'
      ? encodeURIComponent(segment)
      : encodeURIComponent(segment[0]) + '|' + segment[1] + '|' + segment[2]
  ) as SegmentPath
}
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:114-163](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L114-L163)

`discoverValidationDepths` walks loader trees to calculate group depths by URL depth across parallel slots. It tracks how many route group segments exist between URL-consuming segments, ensuring that shallow slots correctly remain in the dynamic stage when they run out of group layers before reaching validation boundaries.

```tsx
export function discoverValidationDepths(loaderTree: LoaderTree): number[] {
  const groupDepthsByUrlDepth: number[] = []

  function recordGroupDepth(urlDepth: number, groupDepth: number): void {
    while (groupDepthsByUrlDepth.length <= urlDepth) {
      groupDepthsByUrlDepth.push(0)
    }
    if (groupDepth > groupDepthsByUrlDepth[urlDepth]) {
      groupDepthsByUrlDepth[urlDepth] = groupDepth
    }
  }

  // urlDepth tracks the index of the current URL-consuming segment.
  // Groups accumulate at the same index. When the next URL segment
  // is reached, it increments the index and resets the group counter.
  // We start at -1 so the root segment '' increments to 0.
  function walk(tree: LoaderTree, urlDepth: number, groupDepth: number): void {
    const segment = tree[0]
    const { parallelRoutes } = parseLoaderTree(tree)
    const consumesDepth = segmentConsumesURLDepth(segment)

    let nextUrlDepth = urlDepth
    let nextGroupDepth = groupDepth
    if (consumesDepth) {
      nextUrlDepth = urlDepth + 1
      nextGroupDepth = 0
      recordGroupDepth(nextUrlDepth, 0)
    } else if (
      typeof segment === 'string' &&
      isGroupSegment(segment) &&
      segment !== '(__SLOT__)'
    ) {
      // Count real route groups but not the synthetic '(__SLOT__)' segment
```
Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:939-971](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L939-L971)

## Development Worker and Error Delivery

### Snapshotting Request Contexts

The dev validation worker executes off the main thread by receiving a serializable payload built via `buildDevValidationSnapshot`. This function inspects the settled `AppRenderContext`, instant inputs, static inputs, prefetch mode, fallback route params, and render error flags to construct a plain data structure containing headers, query parameters, URL pathnames, drafts, HMR refresh metadata, and render options.

```typescript
export async function buildDevValidationSnapshot(
  ctx: AppRenderContext,
  instantInputs: ResolvedValidationInputs | null,
  staticInputs: ResolvedValidationInputs,
  prefetchMode: PrefetchingMode,
  fallbackRouteParams: OpaqueFallbackRouteParams | null,
  devRenderDidError: boolean
): Promise<DevValidationSnapshot> {
```
Sources: [packages/next/src/server/app-render/dev-validation-worker-snapshot.ts:20-27](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/dev-validation-worker-snapshot.ts#L20-L27)

```typescript
return {
  page: ctx.workStore.page,
  route: ctx.workStore.route,
  requestId: ctx.requestId,
  responseFinished,
  prefetchMode,
  devRenderDidError,
  nonce: ctx.nonce,
  query: ctx.query,
  request: {
    headers: [...requestStore.headers.entries()],
    urlPathname: requestStore.url.pathname,
    urlSearch: requestStore.url.search,
    rootParams: requestStore.rootParams,
    isDraftMode: requestStore.draftMode.isEnabled,
    isHmrRefresh: requestStore.isHmrRefresh ?? false,
    hmrRefreshHash: requestStore.hmrRefreshHash,
    requestStartTime: ctx.workStore.requestStartTime,
  },
  interpolatedParams: ctx.interpolatedParams,
  requestFallbackRouteParams: ctx.fallbackRouteParams,
  fallbackRouteParams,
  optimisticRouting: ctx.renderOpts.experimental.optimisticRouting,
  forceStatic: ctx.workStore.forceStatic,
  validationLevel: ctx.workStore.validationLevel,
  implicitTags: ctx.implicitTags.tags,
  additionalClientReferenceManifestPages: ctx.workStore
    .additionalClientReferenceManifestPages
    ? [...ctx.workStore.additionalClientReferenceManifestPages]
    : [],
  reactBrowserBailout: ctx.renderOpts.experimental.reactBrowserBailout,
  isDebugChannelEnabled: !!ctx.renderOpts.setReactDebugChannel,
  renderOpts: {
    images: ctx.renderOpts.images,
    allowEmptyStaticShell: ctx.renderOpts.allowEmptyStaticShell,
    partialPrefetching: ctx.renderOpts.partialPrefetching,
  },
  instantInputs: instantInputs
    ? await toSerializedInputs(instantInputs)
    : null,
  staticInputs: await toSerializedInputs(staticInputs),
}
```
Sources: [packages/next/src/server/app-render/dev-validation-worker-snapshot.ts:61-102](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/dev-validation-worker-snapshot.ts#L61-L102)

> [!NOTE]
> Debug channel streams are drained exactly once into chunk arrays during snapshot creation because instant and static inputs share the same underlying stream instance.

Sources: [packages/next/src/server/app-render/dev-validation-worker-snapshot.ts:32-37](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/dev-validation-worker-snapshot.ts#L32-L37)

### Validation Worker Execution

`runDevValidation` installs SWC native bindings, code-frame support, and a disk source map lookup linked to `distDir`. It loads the target route's compiled module using `loadComponents` and registers additional client reference manifests so cross-page client references resolve correctly inside the worker isolate.

```typescript
export async function runDevValidation(
  message: DevValidationWorkerMessage,
  abortBuffer: SharedArrayBuffer
): Promise<DevValidationWorkerResult> {
  // Load the native SWC bindings and wire the code-frame renderer so the errors
  // logged below render with a source-mapped code frame, matching the
  // in-process dev output (the E2E tests snapshot the CLI text between the
  // validation markers). The `build/swc` graph these pull in is bundled as a
  // runtime external (see `next-runtime.webpack-config.js`), so it resolves
  // from the installed `next/dist` tree rather than being compiled into this
  // worker bundle, the same way the unbundled build worker loads it.
  await installBindings()
  installCodeFrameSupport()
  setBundlerFindSourceMapImplementation(
    createDiskSourceMapLookup(message.distDir)
  )
  setHttpClientAndAgentOptions({
    httpAgentOptions: message.nextConfigSerializable.httpAgentOptions,
  })

  // Populates the manifests singleton for the route via `setManifestsSingleton`
  // inside `loadComponents`, exactly as a real request does. The pool tears the
  // worker down on HMR / route recompile so the next validation reloads from a
  // clean require cache.
  const { ComponentMod } = await loadComponents<AppPageModule>({
    distDir: message.distDir,
    page: message.page,
    isAppPath: true,
    isDev: true,
    sriEnabled: false,
    needsManifestsForLegacyReasons: true,
  })

  await registerAdditionalClientReferenceManifests(
    message.distDir,
    message.additionalClientReferenceManifestPages
  )
```
Sources: [packages/next/src/server/dev/dev-validation-worker.ts:309-345](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker.ts#L309-L345)

```typescript
function createDiskSourceMapLookup(
  distDir: string
): (sourceURL: string) => ModernSourceMapPayload | undefined {
  // The frames carry resolved paths, so compare against the resolved `distDir`
  // to keep the containment check meaningful when the project sits behind a
  // symlink.
  let canonicalDistDir = distDir
  try {
    canonicalDistDir = realpathSync(distDir)
  } catch {}

  const payloads = new Map<string, ModernSourceMapPayload | undefined>()

```
Sources: [packages/next/src/server/dev/dev-validation-worker.ts:53-65](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker.ts#L53-L65)

The worker delegates the validation pass by invoking `ComponentMod.routeModule.runValidationInDev`, passing the component module, message payload, and supersede abort signal. If execution yields errors or completes successfully, the worker encodes them into RSC Flight format before forwarding them to the main thread.

```typescript
// Crossing into the app-page bundle: the entire validation runs there, so
// the client prerenders use the same React the user's client components
// resolve through `ComponentMod`.
const validationErrors = await ComponentMod.routeModule.runValidationInDev(
  ComponentMod,
  message,
  signal
)

```
Sources: [packages/next/src/server/dev/dev-validation-worker.ts:369-377](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker.ts#L369-L377)

> [!WARNING]
> If a supersede signal aborts or validation errors are undefined, `runDevValidation` immediately returns `null` to discard superseded validation jobs.

Sources: [packages/next/src/server/dev/dev-validation-worker.ts:378-380](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/dev-validation-worker.ts#L378-L380)

### Surfacing Diagnostics to the Overlay

Diagnostics and runtime errors discovered during validation are formatted and delivered to the development overlay through specific error container layouts. The dev overlay classifies errors into variants such as metadata, viewport, sync-io, sync-io-client, and unrendered-segment, rendering them within an `ErrorOverlayLayout` paired with an `InstantHeaderExplanation`.

```tsx
case 'dynamic-viewport': {
  switch (errorDetails.variant) {
    case 'link':
      errorMessage = (
        <>
          Next.js encountered URL data in <code>generateViewport()</code>.
        </>
      )
      break
    case 'runtime':
      errorMessage = (
        <>
          Next.js encountered runtime data in{' '}
          <code>generateViewport()</code>.
        </>
      )
      break
    case 'navigation':
      errorMessage = (
        <>
          Next.js encountered <code>navigation()</code> in{' '}
          <code>generateViewport()</code>.
        </>
      )
      break
    case 'prefetch':
      errorMessage = (
        <>
          Next.js encountered <code>prefetch()</code> in{' '}
          <code>generateViewport()</code>.
        </>
      )
      break
    case 'dynamic':
      errorMessage = (
        <>
          Next.js encountered uncached data in{' '}
          <code>generateViewport()</code>.
        </>
      )
      break
    default:
      errorMessage = errorDetails.variant satisfies never
  }
```
Sources: [packages/next/src/next-devtools/dev-overlay/container/errors.tsx:1205-1248](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/container/errors.tsx#L1205-L1248)

```tsx
case 'unrendered-segment':
  return (
    <ErrorOverlayLayout
      errorType={errorType}
      errorMessage="Next.js could not validate that a segment in your UI has instant navigation."
      headerChildren={
        <InstantHeaderExplanation kind="unrendered-segment" />
      }
      renderTabBar={renderTabBar}
      canGoPrevious={canGoPrevious}
      canGoNext={canGoNext}
      onPrevious={handlePrevious}
      onNext={handleNext}
      onClose={isServerError ? undefined : onClose}
      debugInfo={debugInfo}
      error={error}
      runtimeErrors={activeErrors}
      activeIdx={activeIdx}
      setActiveIndex={setActiveIndex}
      dialogResizerRef={dialogResizerRef}
      generateErrorInfo={generateErrorInfo}
      {...props}
    >
      <UnrenderedSegmentInfo
        route={errorDetails.route}
```
Sources: [packages/next/src/next-devtools/dev-overlay/container/errors.tsx:1380-1404](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/container/errors.tsx#L1380-L1404)

## Related

- [Dynamic Rendering](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/dynamic-rendering)
- [App Rendering](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-rendering/app-rendering)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
