vercel/next.js · error · SerializableError

Error serializing props returned from `${method}` in "${page

Error message

Error serializing props returned from `${method}` in "${page}".
Reason: Props must be returned as a plain object from ${method}: `{ props: { ... } }` (received: `${getObjectClassLabel(input)}`).

What it means

Thrown by isSerializableProps() when the value returned from getStaticProps/getServerSideProps (or getInitialProps) is not a plain Object. The framework requires `{ props: { ... } }` where the props bag is a plain object literal; class instances, arrays, Maps, Dates, or wrapped values fail isPlainObject and report the detected class label.

Source

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

const regexpPlainIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/

export class SerializableError extends Error {
  constructor(page: string, method: string, path: string, message: string) {
    super(
      path
        ? `Error serializing \`${path}\` returned from \`${method}\` in "${page}".\nReason: ${message}`
        : `Error serializing props returned from \`${method}\` in "${page}".\nReason: ${message}`
    )
  }
}

export function isSerializableProps(
  page: string,
  method: string,
  input: any
): true {
  if (!isPlainObject(input)) {
    throw new SerializableError(
      page,
      method,
      '',
      `Props must be returned as a plain object from ${method}: \`{ props: { ... } }\` (received: \`${getObjectClassLabel(
        input
      )}\`).`
    )
  }

  function visit(visited: Map<any, string>, value: any, path: string) {
    if (visited.has(value)) {
      throw new SerializableError(
        page,
        method,
        path,
        `Circular references cannot be expressed in JSON (references: \`${
          visited.get(value) || '(self)'
        }\`).`

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Ensure the return shape is exactly `{ props: { ... } }` with a plain object literal as props.
  2. Call .toObject()/.toJSON()/JSON.parse(JSON.stringify(x)) on ORM/model instances before returning them.
  3. Convert Dates to ISO strings and Maps to plain objects in the props mapping.
  4. If returning arrays, wrap them: `{ props: { items: [...] } }`.

Example fix

// before
export async function getStaticProps() {
  const user = await User.findById(id) // Mongoose document
  return { props: { user } } // user is a class instance -> error
}
// after
export async function getStaticProps() {
  const user = await User.findById(id).lean()
  return { props: { user: JSON.parse(JSON.stringify(user)) } }
}
Defensive patterns

Strategy: validation

Validate before calling

function isPlainSerializableProps(x: unknown): x is Record<string, unknown> {
  return typeof x === 'object' && x !== null &&
    Object.getPrototypeOf(x) === Object.prototype || Object.getPrototypeOf(x) === null
}
// in getStaticProps:
if (!isPlainSerializableProps(props)) throw new Error('props must be a plain object')
return { props }

Type guard

function isPlainObject(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: A data-fetching function returns something other than a plain object as props — e.g. returns an array directly, a class instance, a Map, a React element, or forgets the props wrapper. The check runs after the function resolves during build/prerender.

Common situations: Returning a Mongoose document or ORM model instance without `.toObject()`/`.toJSON()`; returning a Date or Map in props; forgetting `{ props: ... }` and returning the data directly; or a serializer that yields a class-typed result.

Related errors


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