{"record":{"id":"86f88cc0e141056b","repo":"remix-run/remix","slug":"csrf-middleware-requires-session-middleware-to-r","errorCode":null,"errorMessage":"csrf middleware requires session() middleware to run before it","messagePattern":"csrf middleware requires session\\(\\) middleware to run before it","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/csrf-middleware/src/lib/csrf.ts","lineNumber":134,"sourceCode":"\n/**\n * Session-backed CSRF protection middleware.\n *\n * This middleware requires the session middleware to run before it.\n *\n * @param options CSRF options\n * @returns CSRF middleware\n */\nexport function csrf(options: CsrfOptions = {}): Middleware {\n  let safeMethods = options.safeMethods ?? defaultSafeMethods\n  let tokenKey = options.tokenKey ?? '_csrf'\n  let fieldName = options.fieldName ?? '_csrf'\n  let headerNames = options.headerNames ?? defaultTokenHeaderNames\n  let allowMissingOrigin = options.allowMissingOrigin ?? true\n\n  return async (context, next) => {\n    if (context.get(Session) == null) {\n      throw new Error('csrf middleware requires session() middleware to run before it')\n    }\n\n    let expectedToken = getCsrfToken(context, tokenKey)\n\n    if (isSafeMethod(context.method, safeMethods)) {\n      return next()\n    }\n\n    let validOrigin = await validateRequestOrigin(\n      context,\n      options.origin,\n      allowMissingOrigin,\n      context.url.origin,\n    )\n    if (!validOrigin) {\n      return getErrorResponse(options, 'invalid-origin', context)\n    }\n","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/csrf-middleware/src/lib/csrf.ts#L116-L152","documentation":"The csrf() middleware stores and verifies CSRF tokens in the user session, so it requires the session() middleware to have run earlier in the request pipeline. At request time it checks context.get(Session); if no session is present it throws this error rather than silently skipping CSRF protection.","triggerScenarios":"Registering csrf() without session() in the middleware chain: e.g. router.use(csrf()) with no prior session() middleware, or ordering csrf() before session() so context.get(Session) is null when csrf runs.","commonSituations":"Adding csrf() to an existing app that never set up sessions; middleware ordering mistakes where session() runs after csrf(); tree-shaking or conditional registration that accidentally drops session().","solutions":["Add session() middleware before csrf(): router.use(session(...), csrf())","Verify middleware order — session() must appear earlier in the chain than csrf()","Check that any conditional middleware registration always includes session() when csrf() is enabled"],"exampleFix":"// before\nrouter.use(csrf())\n// after\nrouter.use(session(sessionOptions), csrf())","handlingStrategy":"validation","validationCode":"const handler = session(sessionOptions).wrap?.(csrf()) // or:\n// verify at setup: router.use(session(...)); router.use(csrf())\n// runtime guard before csrf logic:\nif (context.get(Session) == null) throw new Error('configure session() before csrf()')","typeGuard":"import { Session } from 'remix'\nconst hasSession = (ctx: Request['context']) => ctx.get(Session) != null","tryCatchPattern":"try { await csrfHandler(context, next) } catch (e) { if (e instanceof Error && e.message.includes('session() middleware')) { /* fix middleware order */ } throw e }","preventionTips":["Always register session() before csrf() in one place","Add a smoke test that any csrf-enabled route also has session middleware"],"tags":["csrf-middleware","session","middleware-order","configuration"],"backgroundTag":"missing-middleware-dependency","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}