remix-run/react-router · error · ErrorResponseImpl

Route "${routeId}" does not have ${article} ${type}, but you

Error message

Route "${routeId}" does not have ${article} ${type}, but you are trying to submit to it. To fix this, please add ${article} `${type}` function to the route

What it means

When a form submission (or fetcher submission) targets a route, React Router needs either a server `action` or a `clientAction` on that route to process it. `noActionDefinedError` logs the message and throws an `ErrorResponseImpl(405, "Method Not Allowed")`, which is rendered by the route's ErrorBoundary. The article ("a"/"an") and the function name in the message tell you exactly which export is missing.

Source

Thrown at packages/react-router/lib/dom/ssr/routes.tsx:223

    let fn = type === "action" ? "serverAction()" : "serverLoader()";
    let msg =
      `You are trying to call ${fn} on a route that does not have a server ` +
      `${type} (routeId: "${route.id}")`;
    console.error(msg);
    throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
  }
}

export function noActionDefinedError(
  type: "action" | "clientAction",
  routeId: string,
) {
  let article = type === "clientAction" ? "a" : "an";
  let msg =
    `Route "${routeId}" does not have ${article} ${type}, but you are trying to ` +
    `submit to it. To fix this, please add ${article} \`${type}\` function to the route`;
  console.error(msg);
  throw new ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
}

export function createClientRoutes(
  manifest: RouteManifest<EntryRoute>,
  routeModulesCache: RouteModules,
  initialState: HydrationState | null,
  ssr: boolean,
  isSpaMode: boolean,
  parentId: string = "",
  routesByParentId: Record<
    string,
    Omit<EntryRoute, "children">[]
  > = groupRoutesByParentId(manifest),
  needsRevalidation?: Set<string>,
): DataRouteObject[] {
  return (routesByParentId[parentId] || []).map((route) => {
    let routeModule = routeModulesCache[route.id];

View on GitHub (pinned to 6beaca3952)

Solutions

  1. Add an `action` export to the route the form submits to: `export async function action({ request }: ActionFunctionArgs) { ... }`.
  2. For client-side only handling, export `clientAction` instead.
  3. Verify the form's `action` prop resolves to the route you think it does (check the matched route in dev tools).
  4. If the action lives on a parent layout route, submit to that parent path explicitly.

Example fix

// before
export default function Notes() {
  return <Form method="post"><input name="title" /><button>Save</button></Form>;
}

// after
export async function action({ request }: ActionFunctionArgs) {
  const fd = await request.formData();
  await saveNote(String(fd.get("title")));
  return redirect("/notes");
}
export default function Notes() {
  return <Form method="post"><input name="title" /><button>Save</button></Form>;
}
Defensive patterns

Strategy: validation

Validate before calling

// before rendering an editable form, confirm the route can handle POST
const canSubmit = Boolean(routeModule.action ?? routeModule.clientAction);

Try / catch

try {
  await fetcher.submit(formData, { method: "post" });
} catch (e) {
  if (isResponse(e) && e.status === 405) setFormError("This page cannot save yet.");
  else throw e;
}

Prevention

When it happens

Trigger: Rendering `<Form method="post">` on a route with no `action` and no `clientAction`; a fetcher `fetcher.submit(..., { method: "post" })` pointed at a route without an action; posting to an index route whose parent has the action but the form targets the child; SPA-mode routes where only a clientLoader exists.

Common situations: Starting from a read-only page and adding a settings/logout form without adding an action; pointing `<Form action="/some-path">` at a path whose route lacks an action; upgrading a Remix app where an action was accidentally dropped during refactor; GET submissions (`method="get"`) are fine, so the error appears only after switching the form to POST.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/f07f70ee69dce603. Report an issue: GitHub.