facebook/react · error · Error

Client Functions cannot be passed directly to Server Functio

Error message

Client Functions cannot be passed directly to Server Functions. Only Functions passed from the Server can be passed back again.

What it means

In the Flight reply protocol, functions can travel client-to-server only as server references - functions defined with 'use server', which carry an $$id the server can bind back to an implementation. A plain client-side function has no server identity, so when serializeReply meets one (after the temporary-reference dedupe check fails) it throws with this explanation. The rule keeps the network boundary explicit: code defined on the client never executes on the server.

Source

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

        formData.set(formFieldPrefix + refId, referenceClosureJSON);
        const serverReferenceId = serializeServerReferenceID(refId);
        // Store the server reference ID for deduplication.
        writtenObjects.set(value, serverReferenceId);
        return serverReferenceId;
      }
      if (temporaryReferences !== undefined && key.indexOf(':') === -1) {
        // TODO: If the property name contains a colon, we don't dedupe. Escape instead.
        const parentReference = writtenObjects.get(parent);
        if (parentReference !== undefined) {
          // If the parent has a reference, we can refer to this object indirectly
          // through the property name inside that parent.
          const reference = parentReference + ':' + key;
          // Store this object so that the server can refer to it later in responses.
          writeTemporaryReference(temporaryReferences, reference, value);
          return serializeTemporaryReferenceMarker();
        }
      }
      throw new Error(
        'Client Functions cannot be passed directly to Server Functions. ' +
          'Only Functions passed from the Server can be passed back again.',
      );
    }

    if (typeof value === 'symbol') {
      if (temporaryReferences !== undefined && key.indexOf(':') === -1) {
        // TODO: If the property name contains a colon, we don't dedupe. Escape instead.
        const parentReference = writtenObjects.get(parent);
        if (parentReference !== undefined) {
          // If the parent has a reference, we can refer to this object indirectly
          // through the property name inside that parent.
          const reference = parentReference + ':' + key;
          // Store this object so that the server can refer to it later in responses.
          writeTemporaryReference(temporaryReferences, reference, value);
          return serializeTemporaryReferenceMarker();
        }
      }

View on GitHub (pinned to eafeac097b)

Solutions

  1. Make the function a Server Function: add 'use server' at the top of its module or above the definition, then pass that reference instead of a closure
  2. Replace the callback with data: send a discriminator/ID plus args and have the server switch on it
  3. Expose several named server functions and choose which one to call on the client
  4. Audit nested option objects before sending them - strip or replace any function-valued properties

Example fix

// before
await applyOp(values, x => x * 2); // client closure -> throws

// after
// ops.ts
'use server';
export async function double(x) { return x * 2; }
// client
import {double} from './ops';
await applyOp(values, double); // server reference, OK
Defensive patterns

Strategy: type-guard

Validate before calling

// Scan the argument tree for functions that are not server references
import {isServerReference} from './guards'; // typeGuard below
function assertNoClientFunctions(v: unknown, seen = new Set()): void {
  if (v === null || typeof v !== 'object' && typeof v !== 'function') return;
  seen.add(v);
  if (typeof v === 'function' && !isServerReference(v)) {
    throw new Error('Client function cannot be passed to a Server Function');
  }
  if (typeof v === 'object') {
    Object.values(v).forEach(c => { if (!seen.has(c)) assertNoClientFunctions(c, seen); });
  }
}

Type guard

const SERVER_REFERENCE = Symbol.for('react.server.reference');
const isServerReference = (
  fn: unknown,
): fn is ((...args: any[]) => Promise<any>) & {$$id: string} =>
  typeof fn === 'function' &&
  (fn as any).$$typeof === SERVER_REFERENCE &&
  typeof (fn as any).$$id === 'string';

Prevention

When it happens

Trigger: Passing an arrow function, closure, class method, or callback as a Server Function argument: await runOp(values, x => x * 2); also functions nested inside objects/arrays in the args tree, or a function received in props forwarded to a server action.

Common situations: Attempted callback-style APIs across the network; refactoring local functions into actions and forgetting a call site still passes a client closure; libraries whose option objects accept callbacks being sent wholesale to server functions.

Related errors


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