{"record":{"id":"a73d85cd547f3b80","repo":"overleaf/overleaf","slug":"email-does-not-belong-to-user-belongs-to-userwi","errorCode":null,"errorMessage":"email does not belong to user. Belongs to ${userWithEmail._id}","messagePattern":"email does not belong to user\\. Belongs to (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"services/web/scripts/remove_email.mjs","lineNumber":34,"sourceCode":"\n  // email arg can be within double quotes for arg so that we can handle\n  // malformed emails with spaces\n  email = email.replace(/\"/g, '')\n\n  console.log(\n    `\\nBegin request to remove email \"${email}\" from user \"${userId}\"\\n`\n  )\n\n  const userWithEmail = await UserGetter.promises.getUserByAnyEmail(email, {\n    _id: 1,\n  })\n\n  if (!userWithEmail) {\n    throw new Error(`no user found with email \"${email}\"`)\n  }\n\n  if (userWithEmail._id.toString() !== userId) {\n    throw new Error(\n      `email does not belong to user. Belongs to ${userWithEmail._id}`\n    )\n  }\n\n  const auditLog = {\n    initiatorId: undefined,\n    ipAddress: '0.0.0.0',\n    extraInfo: {\n      script: true,\n    },\n  }\n\n  const skipParseEmail = true\n  await UserUpdater.promises.removeEmailAddress(\n    userId,\n    email,\n    auditLog,\n    skipParseEmail","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/web/scripts/remove_email.mjs#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nnode scripts/remove_email.mjs user@example.com 660f...\n// after\nnode scripts/remove_email.mjs 660f... user@example.com","handlingStrategy":"validation","validationCode":"const owner = await UserGetter.promises.getUserByAnyEmail(email, { _id: 1 })\nif (!owner) throw new Error('email not found')\nif (String(owner._id) !== String(userId)) {\n  throw new Error(`email belongs to ${owner._id}, not ${userId}`)\n}\nawait removeEmail(userId, email)","typeGuard":"function emailBelongsToUser(owner, userId) { return !!owner && String(owner._id) === String(userId) }","tryCatchPattern":"try {\n  await removeEmail(userId, email)\n} catch (err) {\n  const m = err.message.match(/Belongs to ([0-9a-f]{24})/)\n  if (m) console.error(`Wrong owner: email belongs to user ${m[1]}. Re-run with that id if intended.`)\n  else throw err\n}","preventionTips":["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"],"tags":["mongodb","script","argument-mismatch","ownership"],"backgroundTag":"entity-ownership-mismatch","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}