{"record":{"id":"08f7865d2a7921b9","repo":"nextauthjs/next-auth","slug":"cannot-parse-action-at-pathname","errorCode":null,"errorMessage":"Cannot parse action at ${pathname}","messagePattern":"Cannot parse action at (.+?)","errorType":"exception","errorClass":"UnknownAction","httpStatus":null,"severity":"error","filePath":"packages/core/src/lib/utils/web.ts","lineNumber":127,"sourceCode":"/** Web compatible method to create a random string of a given length */\nexport function randomString(size: number) {\n  const i2hex = (i: number) => (\"0\" + i.toString(16)).slice(-2)\n  const r = (a: string, i: number): string => a + i2hex(i)\n  const bytes = crypto.getRandomValues(new Uint8Array(size))\n  return Array.from(bytes).reduce(r, \"\")\n}\n\n/** @internal Parse the action and provider id from a URL pathname. */\nexport function parseActionAndProviderId(\n  pathname: string,\n  base: string\n): {\n  action: AuthAction\n  providerId?: string\n} {\n  const a = pathname.match(new RegExp(`^${base}(.+)`))\n\n  if (a === null) throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  const actionAndProviderId = a.at(-1)!\n\n  const b = actionAndProviderId.replace(/^\\//, \"\").split(\"/\").filter(Boolean)\n\n  if (b.length !== 1 && b.length !== 2)\n    throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  const [action, providerId] = b\n\n  if (!isAuthAction(action))\n    throw new UnknownAction(`Cannot parse action at ${pathname}`)\n\n  if (\n    providerId &&\n    ![\"signin\", \"callback\", \"webauthn-options\"].includes(action)\n  )\n    throw new UnknownAction(`Cannot parse action at ${pathname}`)","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/lib/utils/web.ts#L109-L145","documentation":"parseActionAndProviderId matches the request pathname against the configured basePath using the regex ^{base}(.+); if the pathname does not extend the base path (no trailing segment), the match fails and UnknownAction is thrown. It means the URL carries no actionable segment after the auth base path.","triggerScenarios":"Requesting exactly /api/auth (basePath with no action suffix), or a URL where basePath is misconfigured so the regex does not match, e.g. basePath set to /auth while requests come to /api/auth.","commonSituations":"basePath option not matching the actual mounted route (AuthHandler mounted at /api/auth/[...action] but basePath left as default or vice versa); proxies stripping or rewriting the path.","solutions":["Set config.basePath to the full prefix of the auth route (e.g. \"/api/auth\") so it matches incoming URLs","Request a real action endpoint like /api/auth/session rather than the bare base path","Check proxy/rewrite rules so the original pathname is preserved"],"exampleFix":"// before\nexport const { handlers } = NextAuth({ basePath: \"/auth\" }) // routes at /api/auth/*\n// after\nexport const { handlers } = NextAuth({ basePath: \"/api/auth\" })","handlingStrategy":"validation","validationCode":"const base = \"/api/auth\";\nif (!pathname.startsWith(base) || pathname === base) throw new Error(`URL must include an action after ${base}`);","typeGuard":"null","tryCatchPattern":"try { return await handle(req) } catch (e) { if (String(e.message).startsWith(\"Cannot parse action\")) return new Response(\"Not Found\", { status: 404 }); throw e; }","preventionTips":["Keep config.basePath in sync with the actual mounted route prefix","Always request a concrete action endpoint, never the bare base path","Check reverse-proxy rewrite rules preserve the full pathname"],"tags":["routing","url-parsing","base-path"],"backgroundTag":"auth-route-not-parsed","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}