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,
    skipParseEmail

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. 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
  2. Swap the script arguments so userId comes first and email second, per the script usage
  3. Verify ownership with db.users.findOne({_id: ObjectId('<userId>')}) and confirm which record should lose the email
  4. 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

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


AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03). Data as JSON: /api/errors/a73d85cd547f3b80. Report an issue: GitHub.