vercel/next.js · error · SerializableError

Error serializing `${path}` returned from `${method}` in "${

Error message

Error serializing `${path}` returned from `${method}` in "${page}".
Reason: invariant: Unknown error encountered in Object.

What it means

Thrown by isSerializableProps() as a defensive invariant when an Object passes isPlainObject and begins traversal, but the recursive every() check returns false without any inner check throwing. In normal operation one of the inner SerializableErrors fires first; reaching this branch means the traversal logic encountered an unexpected state, signaling a framework-internal inconsistency rather than a user data problem.

Source

Thrown at packages/next/src/lib/is-serializable-props.ts:98

      visit(refs, value, path)

      if (
        Object.entries(value).every(([key, nestedValue]) => {
          const nextPath = regexpPlainIdentifier.test(key)
            ? `${path}.${key}`
            : `${path}[${JSON.stringify(key)}]`

          const newRefs = new Map(refs)
          return (
            isSerializable(newRefs, key, nextPath) &&
            isSerializable(newRefs, nestedValue, nextPath)
          )
        })
      ) {
        return true
      }

      throw new SerializableError(
        page,
        method,
        path,
        `invariant: Unknown error encountered in Object.`
      )
    }

    if (Array.isArray(value)) {
      visit(refs, value, path)

      if (
        value.every((nestedValue, index) => {
          const newRefs = new Map(refs)
          return isSerializable(newRefs, nestedValue, `${path}[${index}]`)
        })
      ) {
        return true
      }

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Inspect the props object for exotic prototypes (Object.create(null), Proxy, Symbol.toPrimitive) and replace with a plain object literal.
  2. Freeze/clean the data: JSON.parse(JSON.stringify(props)) to strip hidden class info before returning.
  3. If reproducible on plain data, file a Next.js bug with the minimal props shape — this branch is meant to be unreachable.
  4. Update Next.js to the latest patch in case the invariant was fixed.

Example fix

// before — exotic object sneaks through
return { props: { data: Object.create(null) } }
// after — normalize to a plain object
return { props: { data: { ...plainData } } }
Defensive patterns

Strategy: validation

Validate before calling

function normalizeToPlain(obj: unknown): Record<string, unknown> {
  return JSON.parse(JSON.stringify(obj))
}
// return { props: { data: normalizeToPlain(raw) } }

Type guard

function isTrulyPlainObject(v: unknown): v is Record<string, unknown> {
  if (typeof v !== 'object' || v === null) return false
  const proto = Object.getPrototypeOf(v)
  return proto === Object.prototype || proto === null
}

Prevention

When it happens

Trigger: Reachable only if isSerializable returns false for a nested value without throwing — which the code is structured to prevent. Effectively an 'unreachable' guard for an Object branch. If a user sees it, something between isPlainObject and the leaf checks returned an unexpected result.

Common situations: Extremely rare in practice; if observed, suspect a custom Object prototype mutation, a Proxy-wrapped object, or an exotic environment where typeof/Array.isArray behave unexpectedly. Could also indicate a Next.js internal regression in the serializer.

Related errors


AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06). Data as JSON: /api/errors/454163d256bca62d. Report an issue: GitHub.