remix-run/react-router · error · Error
Cookie length will exceed browser maximum. Length: ${seriali
Error message
Cookie length will exceed browser maximum. Length: ${serializedCookie.length} What it means
`createCookieSessionStorage().commitSession` serializes all session data into the cookie and rejects when the serialized cookie exceeds 4096 bytes (the practical browser cookie limit). Cookie-session storage trades backend-free simplicity for a hard size cap; exceeding it would silently truncate the session in the browser.
Source
Thrown at packages/react-router/lib/server-runtime/sessions/cookieStorage.ts:56
Data,
FlashData
> {
let cookie = isCookie(cookieArg)
? cookieArg
: createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
return createSession(
(cookieHeader && (await cookie.parse(cookieHeader, options))) || {},
);
},
async commitSession(session, options) {
let serializedCookie = await cookie.serialize(session.data, options);
if (serializedCookie.length > 4096) {
throw new Error(
"Cookie length will exceed browser maximum. Length: " +
serializedCookie.length,
);
}
return serializedCookie;
},
async destroySession(_session, options) {
return cookie.serialize("", {
...options,
maxAge: undefined,
expires: new Date(0),
});
},
};
}
View on GitHub (pinned to 1fd704a7da)
Solutions
- Move large/per-user data to a server-side store and keep only a session id in the cookie (use `createSessionStorage` with a custom adapter backed by Redis/DB).
- Clear flash data before commit and trim unused fields.
- Reduce cookie options (long `SameSite`, `Domain`, etc.) and shorten the cookie name.
- If you need signed cookies, account for the signature size in your 4096 budget.
Example fix
// before
export const sessionStorage = createCookieSessionStorage({ cookie: { name: '__session' } });
// session.data grows until > 4096 bytes -> throws on commit
// after
export const sessionStorage = createSessionStorage({
cookie: { name: 'sid', secrets: ['...'] },
createData: (data, expires) => redis.set(...),
readData: (id) => redis.get(id),
updateData: (id, data, expires) => redis.set(...),
deleteData: (id) => redis.del(id),
}); Defensive patterns
Strategy: validation
Validate before calling
async function safeCommit(storage: SessionStorage, session: Session, max = 4096) {
const cookie = await storage.commitSession(session);
if (cookie.length > max) throw new Error(`Cookie too large: ${cookie.length}`);
return cookie;
}
// or, proactively estimate before commit:
function estimateCookieBytes(data: unknown) {
return JSON.stringify(data).length + 200; // +overhead/signature headroom
} Try / catch
try {
headers.append('Set-Cookie', await sessionStorage.commitSession(session));
} catch (e) {
if (e instanceof Error && e.message.startsWith('Cookie length will exceed')) {
// spill to server-side store, keep only an id in the cookie
session.id = await persist(session.data);
session.data = {};
headers.append('Set-Cookie', await sessionStorage.commitSession(session));
} else throw e;
} Prevention
- Keep only an id in cookie-session storage; back large data with Redis/DB via `createSessionStorage`.
- Clear flash messages before commit.
- Account for the signature bytes when using signed cookies.
When it happens
Trigger: Calling `commitSession(session)` on a session whose serialized form (data + cookie name + options like `Max-Age`, `Path`, signed HMAC for secret cookies) is over 4096 bytes.
Common situations: Storing growing per-user data (flash messages, cart contents, JSON blobs) in the session; stacking multiple flash messages; a signed cookie where the HMAC signature alone consumes hundreds of bytes; adding many cookie options that bloat the header.
Related errors
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/17573490aa65f297.
Report an issue: GitHub.