facebook/react · error · Error

Only plain objects, and a few built-ins, can be passed to Se

Error message

Only plain objects, and a few built-ins, can be passed to Server Functions. Classes or null prototypes are not supported.

What it means

encodeReply's object branch accepts plain objects and specific built-ins; before accepting a value it checks the prototype chain. If getPrototypeOf(value) is neither Object.prototype nor a supported built-in prototype - i.e. class instances and null-prototype objects - and no temporaryReferences set was passed to stash them, serialization throws with this message. The point is that class identity does not survive the wire, so React refuses rather than silently downgrading your instance to a plain object.

Source

Thrown at packages/react-client/src/ReactFlightReplyClient.js:742

      const getAsyncIterator: void | (() => $AsyncIterator<any, any, any>) = (
        value as any
      )[ASYNC_ITERATOR];
      if (typeof getAsyncIterator === 'function') {
        // We treat AsyncIterables as a Fragment and as such we might need to key them.
        return serializeAsyncIterable(
          value as any,
          getAsyncIterator.call(value as any),
        );
      }

      // Verify that this is a simple plain object.
      const proto = getPrototypeOf(value);
      if (
        proto !== ObjectPrototype &&
        (proto === null || getPrototypeOf(proto) !== null)
      ) {
        if (temporaryReferences === undefined) {
          throw new Error(
            'Only plain objects, and a few built-ins, can be passed to Server Functions. ' +
              'Classes or null prototypes are not supported.' +
              (__DEV__ ? describeObjectForErrorMessage(parent, key) : ''),
          );
        }
        // We will have written this object to the temporary reference set above
        // so we can replace it with a marker to refer to this slot later.
        return serializeTemporaryReferenceMarker();
      }
      if (__DEV__) {
        if ((value as any).$$typeof === REACT_CONTEXT_TYPE) {
          console.error(
            'React Context Providers cannot be passed to Server Functions from the Client.%s',
            describeObjectForErrorMessage(parent, key),
          );
        } else if (objectName(value) !== 'Object') {
          console.error(
            'Only plain objects can be passed to Server Functions from the Client. ' +

View on GitHub (pinned to eafeac097b)

Solutions

  1. Convert to a plain object before the call: {...instance} (spread drops the prototype and methods) or a toJSON()/toPlain() mapper
  2. Rebuild null-prototype objects as normal object literals
  3. If the server must see the actual instance, pass a TemporaryReferenceSet via options so the object is stored and referred to by id
  4. Prefer sending identifiers (IDs) and rehydrating the entity server-side

Example fix

// before
class Cart { constructor(items) { this.items = items; } total() {...} }
await saveCart(new Cart(items)); // throws

// after
await saveCart({items: [...items]}); // plain object literal
// or: await saveCart({...cartInstance});
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the argument tree before calling the server
import {isPlainObjectOrArray} from './guards'; // typeGuard below
function assertReplySerializable(v: unknown, seen = new Set()): void {
  if (v === null || typeof v !== 'object' || seen.has(v)) return;
  seen.add(v);
  if (v instanceof Map || v instanceof Set || v instanceof Date) return; // built-ins OK
  if (!isPlainObjectOrArray(v)) {
    throw new Error(`Non-plain object (${v.constructor?.name}) cannot be sent to a Server Function`);
  }
  Object.values(v).forEach(c => assertReplySerializable(c, seen));
}

Type guard

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

Prevention

When it happens

Trigger: Passing new MyClass(...) (including instances of imported library classes), Object.create(null) objects, or framework/state-library objects as Server Function arguments without a temporaryReferences option; the check fires even when the class is defined in shared code, because instances are not serializable by value.

Common situations: Sending form-state objects created by class-based stores; passing ORM/model or domain entities; structured-clone-style objects with null prototypes produced by libraries; migrating fetch bodies to Server Function args without converting shapes.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/4aaeacc8b0f90a85. Report an issue: GitHub.