nextauthjs/next-auth · error
[updateUser] Missing id
Error message
[updateUser] Missing id
What it means
The Firebase adapter's updateUser requires the incoming partial user to carry an id, because the id is needed to resolve the Firestore document reference (C.users.doc(id)). Without it there is nothing to merge into, so it throws '[updateUser] Missing id'.
Source
Thrown at packages/adapter-firebase/src/index.ts:129
},
async getUserByEmail(email) {
return await getOneDoc(C.users.where("email", "==", email))
},
async getUserByAccount({ provider, providerAccountId }) {
const account = await getOneDoc(
C.accounts
.where("provider", "==", provider)
.where(mapper.toDb("providerAccountId"), "==", providerAccountId)
)
if (!account) return null
return await getDoc(C.users.doc(account.userId))
},
async updateUser(partialUser) {
if (!partialUser.id) throw new Error("[updateUser] Missing id")
const userRef = C.users.doc(partialUser.id)
await userRef.set(partialUser, { merge: true })
const user = await getDoc(userRef)
if (!user) throw new Error("[updateUser] Failed to fetch updated user")
return user
},
async deleteUser(userId) {
await db.runTransaction(async (transaction) => {
const accounts = await C.accounts
.where(mapper.toDb("userId"), "==", userId)
.get()
const sessions = await C.sessions
.where(mapper.toDb("userId"), "==", userId)View on GitHub (pinned to a1a16a5a77)
Solutions
- Always include the document id: updateUser({ id: user.id, ...patch }).
- Resolve the id first via getUserByEmail or getUserByAccount when you only have the email/account.
- Validate incoming payloads in API routes to require id before invoking updateUser.
- Check any mapping layer (Firestore doc -> AdapterUser) preserves the id field.
Example fix
// before
await adapter.updateUser({ name: 'Alice' })
// after
const existing = await adapter.getUserByEmail('alice@example.com')
await adapter.updateUser({ id: existing.id, name: 'Alice' }) Defensive patterns
Strategy: validation
Validate before calling
if (!partialUser?.id) throw new Error('updateUser requires partialUser.id')
await adapter.updateUser(partialUser) Type guard
function isUserWithId(u: Partial<AdapterUser>): u is Partial<AdapterUser> & Pick<AdapterUser, 'id'> {
return typeof u.id === 'string' && u.id.length > 0
} Try / catch
try {
await adapter.updateUser(partialUser)
} catch (e) {
if ((e as Error).message === '[updateUser] Missing id') {
const existing = await adapter.getUserByEmail(partialUser.email!)
if (existing) return adapter.updateUser({ ...partialUser, id: existing.id })
}
throw e
} Prevention
- Resolve ids via getUserByEmail/getUserByAccount before partial updates.
- Validate API payloads with a schema (zod) requiring id for update endpoints.
- Keep the Firestore id mapped to AdapterUser.id in any custom mappers.
When it happens
Trigger: Calling updateUser({ email: '...' }) or updateUser({ id: undefined, ... }) — usually when code spreads a payload whose id was stripped, or when upstream Auth.js events pass a user object that lost its id.
Common situations: Custom JWT/session callbacks that rebuild the user object and omit id; mapping between Firestore document field 'userId' and AdapterUser 'id'; API handlers accepting client JSON that lacks the id field.
Related errors
- [createUser] Failed to fetch created user
- [updateUser] Failed to fetch updated user
- [createSession] Failed to fetch created session
- [updateSession] Failed to fetch updated session
- FirebaseAdapter: value for key "${key}" is undefined
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/87ceef077161e13e.
Report an issue: GitHub.