overleaf/overleaf · error · Error
email does not belong to user. Belongs to ${userWithEmail._i
Error message
email does not belong to user. Belongs to ${userWithEmail._id} What it means
remove_email.mjs verifies that the supplied email actually belongs to the supplied userId before deleting it. When getUserByAnyEmail returns a user whose _id differs from the requested userId, this error is thrown with the real owner's ObjectId embedded in the message. It prevents deleting an email from the wrong account.
Source
Thrown at services/web/scripts/remove_email.mjs:34
// email arg can be within double quotes for arg so that we can handle
// malformed emails with spaces
email = email.replace(/"/g, '')
console.log(
`\nBegin request to remove email "${email}" from user "${userId}"\n`
)
const userWithEmail = await UserGetter.promises.getUserByAnyEmail(email, {
_id: 1,
})
if (!userWithEmail) {
throw new Error(`no user found with email "${email}"`)
}
if (userWithEmail._id.toString() !== userId) {
throw new Error(
`email does not belong to user. Belongs to ${userWithEmail._id}`
)
}
const auditLog = {
initiatorId: undefined,
ipAddress: '0.0.0.0',
extraInfo: {
script: true,
},
}
const skipParseEmail = true
await UserUpdater.promises.removeEmailAddress(
userId,
email,
auditLog,
skipParseEmailView on GitHub (pinned to 28ad3b03b7)
Solutions
- Read the ObjectId in the error message and use that user's id if the intent was to remove the email from its actual owner
- Swap the script arguments so userId comes first and email second, per the script usage
- Verify ownership with db.users.findOne({_id: ObjectId('<userId>')}) and confirm which record should lose the email
- If a merged/duplicated account is involved, fix the data first or pick the correct userId
Example fix
// before node scripts/remove_email.mjs user@example.com 660f... // after node scripts/remove_email.mjs 660f... user@example.com
Defensive patterns
Strategy: validation
Validate before calling
const owner = await UserGetter.promises.getUserByAnyEmail(email, { _id: 1 })
if (!owner) throw new Error('email not found')
if (String(owner._id) !== String(userId)) {
throw new Error(`email belongs to ${owner._id}, not ${userId}`)
}
await removeEmail(userId, email) Type guard
function emailBelongsToUser(owner, userId) { return !!owner && String(owner._id) === String(userId) } Try / catch
try {
await removeEmail(userId, email)
} catch (err) {
const m = err.message.match(/Belongs to ([0-9a-f]{24})/)
if (m) console.error(`Wrong owner: email belongs to user ${m[1]}. Re-run with that id if intended.`)
else throw err
} Prevention
- Pass arguments in the documented order (userId first, email second)
- Parse the owner id from the error message and re-check the ticket
- Verify ownership with a direct DB lookup before deletion
- Watch for accounts re-registered with the same email after merges
When it happens
Trigger: Calling removeEmail(userId, email) where the email resolves to a different user's account — e.g. swapped argument order on the CLI, the email was later re-registered to another account, or the userId was copied from the wrong ticket/row.
Common situations: Args passed to the script in the wrong order (email first, id second); support ticket references an old owner while the email now belongs to a user who re-registered; duplicate user merge left the email pointing at the other record.
Related errors
- failed to clear some sessions, see above for details
- no user found with email "${email}"
- user ${userId} does not exist
- user ${userId} has no enrollment
- user ID not valid: ${userId}
AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03).
Data as JSON: /api/errors/a73d85cd547f3b80.
Report an issue: GitHub.