What Causes This Error?
This error occurs when you try to modify session data directly instead of using the proper session.update() method. H3 sessions are designed to be immutable for safety.
The Problem
// server/api/user.ts
export default defineEventHandler(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ❌ Wrong - direct modification
session.data.userId = 'new-id'
session.data.name = 'John'
// ❌ Wrong - object spread doesn't persist
Object.assign(session.data, { userId: 'new-id' })
})
The Fix
Use session.update()
// server/api/user.ts
export default defineEventHandler(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ✅ Correct - use update method
await session.update({
userId: 'new-id',
name: 'John'
})
return { success: true }
})
Partial Updates
// server/api/update-name.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ✅ Merge with existing data
await session.update({
...session.data,
name: body.name
})
return { success: true }
})
Common Patterns
Login
// server/api/auth/login.post.ts
export default defineEventHandler(async (event) => {
const { email, password } = await readBody(event)
// Validate credentials...
const user = await validateUser(email, password)
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ✅ Set session data
await session.update({
userId: user.id,
email: user.email,
role: user.role,
loginAt: Date.now()
})
return { user: { id: user.id, email: user.email } }
})
Update Profile
// server/api/profile.put.ts
export default defineEventHandler(async (event) => {
const updates = await readBody(event)
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
if (!session.data.userId) {
throw createError({ statusCode: 401 })
}
// Update user in database...
// ✅ Update session with new data
await session.update({
...session.data,
email: updates.email ?? session.data.email,
name: updates.name ?? session.data.name,
updatedAt: Date.now()
})
return { success: true }
})
Logout
// server/api/auth/logout.post.ts
export default defineEventHandler(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ✅ Clear session completely
await session.clear()
return { success: true }
})
Increment Counter
// server/api/visit.post.ts
export default defineEventHandler(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ✅ Update with new value
const currentCount = session.data.visits || 0
await session.update({
...session.data,
visits: currentCount + 1,
lastVisit: Date.now()
})
return { visits: currentCount + 1 }
})
Session Helper Utility
// server/utils/session.ts
import type { H3Event } from 'h3'
interface SessionData {
userId?: string
email?: string
role?: string
[key: string]: unknown
}
export async function getSession(event: H3Event) {
return await useSession<SessionData>(event, {
password: process.env.SESSION_SECRET!,
maxAge: 60 * 60 * 24 * 7
})
}
export async function setSessionData(
event: H3Event,
data: Partial<SessionData>
) {
const session = await getSession(event)
await session.update({
...session.data,
...data
})
return session.data
}
export async function clearSession(event: H3Event) {
const session = await getSession(event)
await session.clear()
}
Usage:
// server/api/example.ts
export default defineEventHandler(async (event) => {
await setSessionData(event, { role: 'admin' })
return { success: true }
})
Reading Session (No Error)
Reading session data doesn’t require update:
// server/api/user.get.ts
export default defineEventHandler(async (event) => {
const session = await useSession(event, {
password: process.env.SESSION_SECRET!
})
// ✅ Reading is fine
if (!session.data.userId) {
throw createError({ statusCode: 401 })
}
return {
userId: session.data.userId,
email: session.data.email
}
})
TypeScript Interface
// types/session.d.ts
declare module 'h3' {
interface SessionData {
userId?: string
email?: string
role?: 'user' | 'admin'
loginAt?: number
}
}
Quick Checklist
- Use
session.update({...})to modify data - Don’t assign directly to
session.data.* - Spread existing data for partial updates
- Use
session.clear()to remove all data - Reading
session.datais safe without update - Create helper utilities for cleaner code