remix-run/react-router · error · Error

Cannot handle form actions without a decodeAction function

Error message

Cannot handle form actions without a decodeAction function

What it means

`handleServerAction` got a form-encoded request whose keys include `$ACTION_*` (React's form-state action protocol), which means a progressive-enhancement form action needs to be decoded. The runtime refused because `decodeAction` was not provided, so it cannot reconstruct the action callable from the formData.

Source

Thrown at packages/react-router/lib/rsc/server.rsc.ts:723

    let formData =
      maybeFormData &&
      typeof maybeFormData === "object" &&
      maybeFormData instanceof FormData
        ? maybeFormData
        : null;

    let skipRevalidation = formData?.has("$SKIP_REVALIDATION") ?? false;

    return {
      actionResult,
      revalidationRequest: getRevalidationRequest(),
      skipRevalidation,
    };
  } else if (isFormRequest) {
    const formData = await request.clone().formData();
    if (Array.from(formData.keys()).some((k) => k.startsWith("$ACTION_"))) {
      if (!decodeAction) {
        throw new Error(
          "Cannot handle form actions without a decodeAction function",
        );
      }
      const action = await decodeAction(formData);
      let formState = undefined;
      try {
        let result = await action();
        if (isRedirectResponse(result)) {
          result = prependBasenameToRedirectResponse(result, basename);
        }
        formState = await decodeFormState?.(result, formData);
      } catch (error) {
        if (isRedirectResponse(error)) {
          return prependBasenameToRedirectResponse(error, basename);
        }
        if (isResponse(error)) {
          return error;
        }

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Pass `decodeAction` (and ideally `decodeFormState`) from your `react-server-dom-*/server` module when constructing the RSC request handler.
  2. Make sure the rsd version matches React Router's RSC runtime version so all four decode functions are present.
  3. If you do not use progressive-enhancement form actions, ensure clients send enhanced (rsc-action-id) requests instead, or route forms to a non-RSC endpoint.

Example fix

// before
handleServerAction({ request, decodeReply, loadServerAction });

// after
import { decodeReply, loadServerAction, decodeAction, decodeFormState } from 'react-server-dom-webpack/server';
handleServerAction({ request, decodeReply, loadServerAction, decodeAction, decodeFormState });
Defensive patterns

Strategy: validation

Validate before calling

function assertFormActionFns(opts: { decodeAction?: unknown }) {
  if (!opts.decodeAction) throw new Error('Form actions require decodeAction');
}
assertFormActionFns({ decodeAction });

Type guard

function hasFormActionFns(o: any): o is { decodeAction: Function } {
  return typeof o.decodeAction === 'function';
}

Prevention

When it happens

Trigger: A POST hits `handleServerAction` from a `<form action>` (or `useFormState`/`useFormStatus`) submission carrying `$ACTION_ID`/`$ACTION_REF`/etc. keys, but the caller did not supply `decodeAction` from `react-server-dom-xyz/server`.

Common situations: RSC setup that wired enhanced actions (`decodeReply`+`loadServerAction`) but forgot the form-action decoder; mixing a custom form handler with the RSC handler; mismatched versions where `decodeAction` isn't exported under the expected name.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/b3cfa253d85014b5. Report an issue: GitHub.