{"record":{"id":"29b433cef55054c2","repo":"remix-run/remix","slug":"session-is-not-started-use-session-middleware-b","errorCode":null,"errorMessage":"Session is not started. Use session() middleware before csrf().","messagePattern":"Session is not started\\. Use session\\(\\) middleware before csrf\\(\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/csrf-middleware/src/lib/csrf.ts","lineNumber":181,"sourceCode":"    return next()\n  }\n}\n\nfunction isSafeMethod(method: string, safeMethods: readonly RequestMethod[]): boolean {\n  return isRequestMethod(method) && safeMethods.includes(method)\n}\n\n/**\n * Gets the CSRF token from the session. Creates one if missing.\n *\n * @param context Request context with a started session\n * @param tokenKey Session key that stores the token\n * @returns The active CSRF token\n */\nexport function getCsrfToken(context: AnyRequestContext, tokenKey = '_csrf'): string {\n  let session = context.get(Session)\n  if (session == null) {\n    throw new Error('Session is not started. Use session() middleware before csrf().')\n  }\n\n  let token = session.get(tokenKey)\n  if (typeof token === 'string' && token !== '') {\n    return token\n  }\n\n  let createdToken = createCsrfToken()\n  session.set(tokenKey, createdToken)\n\n  return createdToken\n}\n\nfunction createCsrfToken(): string {\n  let bytes = new Uint8Array(32)\n  crypto.getRandomValues(bytes)\n\n  let token = ''","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/csrf-middleware/src/lib/csrf.ts#L163-L199","documentation":"getCsrfToken reads the CSRF token from the session attached to the request context and throws if context.get(Session) is null. This is the helper-level counterpart to the csrf() middleware ordering error: any manual call to getCsrfToken (e.g. to render a token into a form) requires session() middleware to have run first.","triggerScenarios":"Calling getCsrfToken(context) inside a component, action, or loader when session() middleware was not registered or ran after the calling code; also called from routes that bypass the middleware chain.","commonSituations":"Rendering a hidden '_csrf' input in a form template and calling the helper before sessions are set up; refactoring routes out from under the session middleware; unit tests invoking helpers with a bare context that has no Session attached.","solutions":["Ensure session() middleware runs before any code calling getCsrfToken","If calling manually outside the middleware chain, attach or create a Session on the context first","In tests, construct a context with a Session instance before invoking the helper"],"exampleFix":"// before\nexport async function action({ context }: RouteArgs) {\n  let token = getCsrfToken(context) // throws if session() not registered\n}\n// after\nrouter.use(session())\nexport async function action({ context }: RouteArgs) {\n  let token = getCsrfToken(context)\n}","handlingStrategy":"type-guard","validationCode":"import { Session } from 'remix'\nif (context.get(Session) == null) {\n  throw new Error('session() middleware must run before using getCsrfToken')\n}","typeGuard":"import { Session } from 'remix'\nfunction hasSession(context: Request['context']): boolean {\n  return context.get(Session) != null\n}","tryCatchPattern":"let token: string | undefined\ntry { token = getCsrfToken(context) } catch (e) { if (e instanceof Error && e.message.includes('session() middleware')) token = undefined; else throw e }","preventionTips":["Only call getCsrfToken inside routes covered by session() middleware","In tests, build contexts with a Session instance"],"tags":["csrf-middleware","session","helper","middleware-order"],"backgroundTag":"missing-middleware-dependency","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}