{"record":{"id":"8c9337fdeb32536c","repo":"medusajs/medusa","slug":"the-user-is-already-authenticated-and-cannot-accep","errorCode":null,"errorMessage":"The user is already authenticated and cannot accept an invite.","messagePattern":"The user is already authenticated and cannot accept an invite\\.","errorType":"http","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/medusa/src/api/admin/invites/accept/route.ts","lineNumber":17,"sourceCode":"import { acceptInviteWorkflow } from \"@medusajs/core-flows\"\nimport { HttpTypes, InviteWorkflow } from \"@medusajs/framework/types\"\nimport { MedusaError } from \"@medusajs/framework/utils\"\nimport {\n  AuthenticatedMedusaRequest,\n  MedusaResponse,\n} from \"@medusajs/framework/http\"\n\nexport const POST = async (\n  req: AuthenticatedMedusaRequest<\n    HttpTypes.AdminAcceptInvite,\n    HttpTypes.AdminGetInviteAcceptParams\n  >,\n  res: MedusaResponse<HttpTypes.AdminAcceptInviteResponse>\n) => {\n  if (req.auth_context.actor_id) {\n    throw new MedusaError(\n      MedusaError.Types.INVALID_DATA,\n      \"The user is already authenticated and cannot accept an invite.\"\n    )\n  }\n\n  const input = {\n    invite_token: req.filterableFields.token as string,\n    auth_identity_id: req.auth_context.auth_identity_id,\n    user: req.validatedBody,\n  } as InviteWorkflow.AcceptInviteWorkflowInputDTO\n\n  let users\n\n  try {\n    const { result } = await acceptInviteWorkflow(req.scope).run({ input })\n    users = result\n  } catch (e) {\n    res.status(401).json({ message: \"Unauthorized\" })","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/medusa/src/api/admin/invites/accept/route.ts#L1-L35","documentation":"Thrown by POST /admin/invites/accept when req.auth_context.actor_id is set, meaning the request is already authenticated. Invite acceptance must happen on an anonymous session so the new user can be created from the token; a logged-in actor cannot consume it.","triggerScenarios":"Calling POST /admin/invites/accept with an Authorization: Bearer <jwt> header or a valid admin session cookie attached.","commonSituations":"Opening an invite link while already logged into the admin in the same browser, SDK clients that set a default auth header on all requests, or tests that attach a token globally.","solutions":["Remove the Authorization header and session cookies before calling accept (use an unauthenticated client/fetch)","Log out of the admin dashboard before opening the invite link","In tests, build a fresh SDK/fetch instance without auth for the accept call"],"exampleFix":"// before\nawait sdk.client.fetch(\"/admin/invites/accept\", { method: \"POST\", body: { token } }) // sdk sends default auth header\n\n// after\nawait fetch(`${baseUrl}/admin/invites/accept`, {\n  method: \"POST\",\n  headers: { \"content-type\": \"application/json\" },\n  body: JSON.stringify({ token }),\n})","handlingStrategy":"validation","validationCode":"const headers: Record<string, string> = {}\ndelete headers[\"authorization\"]\n// use a bare fetch / fresh client with no token:\nawait fetch(`${baseUrl}/admin/invites/accept`, { method: \"POST\", body: JSON.stringify({ token }) })","typeGuard":null,"tryCatchPattern":"try {\n  await acceptInvite(token) // unauthenticated client\n} catch (e: any) {\n  if (e.statusCode === 400 && /already authenticated/.test(e.message)) {\n    logoutThenRetry()\n  } else throw e\n}","preventionTips":["Never attach default auth headers to invite-accept clients","Log out before opening invite links","In tests, construct a separate SDK without auth for acceptance"],"tags":["admin","invites","auth","session"],"backgroundTag":"already-authenticated","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}