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
- Pass `decodeAction` (and ideally `decodeFormState`) from your `react-server-dom-*/server` module when constructing the RSC request handler.
- Make sure the rsd version matches React Router's RSC runtime version so all four decode functions are present.
- 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
- Pass `decodeAction` (and `decodeFormState`) when constructing the RSC request handler.
- Match rsd and React Router RSC runtime versions.
- If you don't support form actions, ensure clients don't submit `$ACTION_*` form data.
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
- Cannot handle enhanced server action without decodeReply and
- The "@vitejs/plugin-rsc" plugin should be placed after the R
- ${configResult.error}
- When using the React Router `basename` and the Vite `base` c
- React Router presets must have a `name` property defined.
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/b3cfa253d85014b5.
Report an issue: GitHub.