How to Fix: Cookie size exceeding 10KB causing AWS Lambda invocation error with NextAuth session
A NextAuth session cookie that grows past browser and infrastructure limits can break authentication in a way that looks unrelated: the login succeeds, but the next request fails when AWS Lambda receives headers that are too large. In this case, the session payload becomes big enough to push the Cookie header beyond practical limits, triggering invocation errors on routes like the feed page.
Understanding the Root Cause
The failure happens because NextAuth stores session-related data in cookies, and cookie-based auth has strict size constraints. Browsers typically enforce about 4KB per cookie, while upstream systems such as proxies, CDNs, and AWS Lambda also impose limits on total request header size. If your authentication flow serializes too much user data into the token or session, each request to your app sends that oversized payload back in the Cookie header.
With Google authentication, this usually appears when the jwt or session callback copies large profile objects, access tokens, refresh tokens, nested permissions, feature flags, organization data, or app-specific metadata into the session. Even if the app appears to work right after sign-in, the next page load can fail because the browser includes the full cookie set on every request.
On serverless deployments, this is more visible because AWS Lambda and related edge infrastructure reject requests with oversized headers before your page logic can recover. That is why the symptom shows up as an invocation error rather than a clean authentication error.
The key principle is simple: store only the minimum identity data in the cookie. Everything else should be fetched server-side from a database, cache, or API when needed.
Step-by-Step Solution
The reliable fix is to shrink the session payload and stop persisting large objects in the NextAuth JWT/session callbacks.
1. Audit what you store in the token and session
Check your NextAuth config for callbacks that spread the entire user, account, or profile object into the token.
callbacks: {
async jwt({ token, user, account, profile }) {
if (user) {
token.user = user
token.account = account
token.profile = profile
}
return token
},
async session({ session, token }) {
session.user = token.user
session.account = token.account
session.profile = token.profile
return session
}
}
This pattern is the usual cause. It serializes far too much data into cookies.
2. Keep only essential fields
Replace broad object copies with a minimal set such as user id, email, name, image, and maybe a short role string.
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
session: {
strategy: "jwt",
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id
token.name = user.name
token.email = user.email
token.picture = user.image
}
return token
},
async session({ session, token }) {
session.user = {
...session.user,
id: token.id,
name: token.name,
email: token.email,
image: token.picture,
}
return session
},
},
})
This keeps the cookie compact and avoids sending unnecessary auth metadata to every page request.
3. Do not store provider tokens unless absolutely required
Access tokens, refresh tokens, and full provider profiles can add significant size. If you need them for backend calls, prefer storing them in a database keyed by user id rather than inside the browser cookie.
callbacks: {
async jwt({ token, user, account }) {
if (user) {
token.id = user.id
}
// Avoid this unless there is a very specific reason
// token.accessToken = account?.access_token
// token.refreshToken = account?.refresh_token
return token
}
}
4. Switch to database-backed sessions if your app needs richer session state
If your app truly requires more user context, use a database session strategy so the cookie only contains a compact session identifier.
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
export default NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
session: {
strategy: "database",
},
callbacks: {
async session({ session, user }) {
session.user.id = user.id
return session
},
},
})
This is often the best option when users belong to multiple organizations, have large permission sets, or require many custom claims.
5. Fetch extended user data after authentication
Instead of packing large objects into the session, fetch them in your API route, server component, or page loader using the authenticated user id.
import { getServerSession } from "next-auth"
import { authOptions } from "@/lib/auth"
import { prisma } from "@/lib/prisma"
export async function GET() {
const session = await getServerSession(authOptions)
if (!session?.user?.id) {
return new Response("Unauthorized", { status: 401 })
}
const userData = await prisma.user.findUnique({
where: { id: session.user.id },
include: {
organizations: true,
preferences: true,
},
})
return Response.json(userData)
}
This pattern moves large state to the server, where it belongs.
6. Verify cookie size in the browser
After updating your config, inspect the auth cookies in browser devtools under Application or Storage. Confirm that the NextAuth session cookie is significantly smaller and that the total request headers no longer spike after login.
7. Clear old cookies before retesting
Browsers can continue sending stale oversized cookies until they are removed. Sign out, clear site cookies, and log in again before validating the fix.
// Optional sign-out trigger in UI
import { signOut } from "next-auth/react"
await signOut({ callbackUrl: "/signin" })
Common Edge Cases
1. Chunked cookies still fail upstream. Some auth libraries split large values across multiple cookies. That may bypass a single-cookie limit but still exceed total header size, so the Lambda error remains.
2. Custom claims keep growing over time. Teams often append roles, tenants, experiments, entitlement lists, and onboarding state into the token. Each small addition looks harmless until production traffic starts failing.
3. Multiple subdomain cookies compound the issue. If the app sets additional cookies for analytics, feature flags, or legacy auth, the total request header can exceed limits faster than expected.
4. Refresh tokens are especially dangerous in cookies. They are often long strings and should generally stay server-side.
5. Middleware and edge routes can surface the bug first. Since they run before page rendering, any oversized cookie may fail on protected routes, redirects, or feed endpoints before React even loads.
6. Old session schema assumptions can break the UI. If you remove fields from the session, make sure components stop reading data that is no longer embedded and instead fetch it from an API.
FAQ
Why does login work but the next page fails?
The sign-in handshake can complete, but subsequent requests include the full stored cookie payload. Once that payload exceeds browser or AWS Lambda header limits, navigation to protected pages fails.
Is increasing AWS limits the right fix?
No. Even if you adjust infrastructure in some environments, oversized auth cookies remain fragile and can still fail in browsers, proxies, or CDNs. The durable fix is to reduce cookie size or move session state to a database.
Should I use JWT sessions or database sessions with NextAuth?
Use JWT sessions only when the session payload is very small. If your app needs provider tokens, permissions, organization data, or other rich state, database sessions are safer and more scalable.
The practical resolution for this issue is to treat the session cookie as a tiny identity envelope, not a transport for full user state. Once the NextAuth callbacks are trimmed and large fields are moved server-side, the AWS Lambda invocation error disappears and authenticated navigation becomes stable again.