facebook/react · error · Error

Invalid server action

Error message

Invalid server action: ${ref}

What it means

On the Parcel client, resolveServerReference splits a serialized id '<moduleId>#<exportName>' and requires bundlerConfig[moduleId] — the Server Manifest entry describing which bundles to load. The manifest is the second argument passed to createFromFetch/createFromNodeStream/createFromReadableStream. An unknown id means the client cannot locate the module implementing the action, so the call is rejected before any request is made.

Solutions

  1. Rebuild so the server manifest includes the new action's module id, and pass that manifest as the second argument to createFrom*
  2. Verify the id before calling: const id = ref.slice(0, ref.lastIndexOf('#')); if (!manifest[id]) the manifest is stale
  3. Make the payload and the manifest come from the same build artifact so ids never drift

Example fix

// before — manifest predates the action
const data = createFromFetch(fetch('/endpoint'), staleServerManifest);
await data.saveNote(); // Invalid server action: notes/saveNote#saveNote

// after — payload and manifest ship from the same build
const build = await import(`./build/manifest-${BUILD_ID}.js`);
const data = createFromFetch(fetch('/endpoint'), build.serverManifest);
await data.saveNote();
Defensive patterns

Strategy: validation

Validate before calling

export function isKnownServerAction(manifest, ref) {
  const idx = ref.lastIndexOf('#');
  return idx > 0 && Object.prototype.hasOwnProperty.call(manifest, ref.slice(0, idx));
}
// before invoking: if (!isKnownServerAction(serverManifest, id)) { refresh the manifest or surface an error }

Type guard

export function parseServerReference(ref) {
  const idx = ref.lastIndexOf('#');
  return idx > 0 ? {id: ref.slice(0, idx), name: ref.slice(idx + 1)} : null;
}

Try / catch

try {
  await action(...args);
} catch (e) {
  if (/Invalid server action/.test(e.message)) {
    // manifest drift — reload the manifest for the current build and retry once
  }
  throw e;
}

Prevention

When it happens

Trigger: Invoking a server function whose module id is absent from the manifest passed to createFrom*; a ref without a '#' so the parsed id is garbage; a manifest generated by an older build than the payload that references the action.

Common situations: Adding a new server action but shipping a stale client manifest; build pipelines emitting client and server artifacts from different revisions; passing the client-reference manifest where the server manifest belongs.

Related errors


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

Appendix: source

Thrown at packages/react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel.js:62

export function resolveClientReference<T>(
  bundlerConfig: null,
  metadata: ClientReferenceMetadata,
): ClientReference<T> {
  // Reference is already resolved during the build.
  return metadata;
}

export function resolveServerReference<T>(
  bundlerConfig: ServerManifest,
  ref: ServerReferenceId,
): ClientReference<T> {
  const idx = ref.lastIndexOf('#');
  const id = ref.slice(0, idx);
  const name = ref.slice(idx + 1);
  const bundles = bundlerConfig[id];
  if (!bundles) {
    throw new Error('Invalid server action: ' + ref);
  }
  return [id, name, bundles];
}

export function preloadModule<T>(
  metadata: ClientReference<T>,
): null | Thenable<any> {
  if (metadata[IMPORT_MAP]) {
    parcelRequire.extendImportMap(metadata[IMPORT_MAP]);
  }

  if (metadata[BUNDLES].length === 0) {
    return null;
  }

  return Promise.all(metadata[BUNDLES].map(url => parcelRequire.load(url)));
}

View on GitHub (pinned to eafeac097b)