remix-run/remix · critical · Error
Session is not started. Use session() middleware before csrf
Error message
Session is not started. Use session() middleware before csrf().
What it means
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.
Source
Thrown at packages/csrf-middleware/src/lib/csrf.ts:181
return next()
}
}
function isSafeMethod(method: string, safeMethods: readonly RequestMethod[]): boolean {
return isRequestMethod(method) && safeMethods.includes(method)
}
/**
* Gets the CSRF token from the session. Creates one if missing.
*
* @param context Request context with a started session
* @param tokenKey Session key that stores the token
* @returns The active CSRF token
*/
export function getCsrfToken(context: AnyRequestContext, tokenKey = '_csrf'): string {
let session = context.get(Session)
if (session == null) {
throw new Error('Session is not started. Use session() middleware before csrf().')
}
let token = session.get(tokenKey)
if (typeof token === 'string' && token !== '') {
return token
}
let createdToken = createCsrfToken()
session.set(tokenKey, createdToken)
return createdToken
}
function createCsrfToken(): string {
let bytes = new Uint8Array(32)
crypto.getRandomValues(bytes)
let token = ''View on GitHub (pinned to 9696913134)
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
Example fix
// before
export async function action({ context }: RouteArgs) {
let token = getCsrfToken(context) // throws if session() not registered
}
// after
router.use(session())
export async function action({ context }: RouteArgs) {
let token = getCsrfToken(context)
} Defensive patterns
Strategy: type-guard
Validate before calling
import { Session } from 'remix'
if (context.get(Session) == null) {
throw new Error('session() middleware must run before using getCsrfToken')
} Type guard
import { Session } from 'remix'
function hasSession(context: Request['context']): boolean {
return context.get(Session) != null
} Try / catch
let token: string | undefined
try { token = getCsrfToken(context) } catch (e) { if (e instanceof Error && e.message.includes('session() middleware')) token = undefined; else throw e } Prevention
- Only call getCsrfToken inside routes covered by session() middleware
- In tests, build contexts with a Session instance
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- csrf middleware requires session() middleware to run before
- Session cookie must be signed
- Session cookie "${sessionCookie.name}" is configured with ht
- Session ID ${session.deleteId} was regenerated, but the old
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/29b433cef55054c2.
Report an issue: GitHub.