overleaf/overleaf · error · NotFoundError
invalid data
Error message
invalid data
What it means
NotFoundError('invalid data') is thrown by confirmEmailFromToken when the token's payload lacks a usable user_id or the embedded email does not match its own normalized parse (email !== EmailHelper.parseEmail(email)). This is a sanity check on the token's stored data before any session or DB work. It indicates a corrupted or forged/malformed token payload.
Source
Thrown at services/web/app/src/Features/User/UserEmailsConfirmationHandler.mjs:51
confirmCodeExpiresTimestamp,
}
}
async function confirmEmailFromToken(req, token) {
const { data } = await OneTimeTokenHandler.promises.peekValueFromToken(
TOKEN_USE,
token
)
if (!data) {
throw new Errors.NotFoundError('no token found')
}
const loggedInUserId = SessionManager.getLoggedInUserId(req.session)
// user_id may be stored as an ObjectId or string
const userId = data.user_id?.toString()
const email = data.email
if (!userId || email !== EmailHelper.parseEmail(email)) {
throw new Errors.NotFoundError('invalid data')
}
if (loggedInUserId !== userId) {
throw new Errors.ForbiddenError('logged in user does not match token user')
}
const user = await UserGetter.promises.getUser(userId, { emails: 1 })
if (!user) {
throw new Errors.NotFoundError('user not found')
}
const emailExists = user.emails.some(emailData => emailData.email === email)
if (!emailExists) {
throw new Errors.NotFoundError('email missing for user')
}
await OneTimeTokenHandler.promises.expireToken(TOKEN_USE, token)
await UserUpdater.promises.confirmEmail(userId, email)
return { userId, email }
}View on GitHub (pinned to 28ad3b03b7)
Solutions
- Re-issue the confirmation token via the normal sendConfirmationCode flow
- Inspect what the current sendConfirmationCode writes into the token payload and confirm field names (user_id, email)
- Check for version drift: tokens created before an upgrade may have a different payload shape — invalidate them
- Ensure emails are normalized (parseEmail) at token-creation time, not just at confirmation time
Example fix
// before
// token payload stored under different keys in old version
await OneTimeTokenHandler.promises.setValueFromToken(TOKEN_USE, token, { uid: userId, mail: email })
// after
await OneTimeTokenHandler.promises.setValueFromToken(TOKEN_USE, token, {
user_id: userId.toString(),
email: EmailHelper.parseEmail(email),
}) Defensive patterns
Strategy: validation
Validate before calling
if (!tokenData?.user_id?.toString() || tokenData.email !== EmailHelper.parseEmail(tokenData.email)) {
throw new Error('token payload invalid')
} Type guard
function hasValidTokenData(data) {
return Boolean(data && data.user_id && data.email && data.email === EmailHelper.parseEmail(data.email))
} Try / catch
try {
await UserEmailsConfirmationHandler.promises.confirmEmailFromToken(req, token)
} catch (err) {
if (err instanceof Errors.NotFoundError && err.message === 'invalid data') { /* issue fresh token */ }
else throw err
} Prevention
- Normalize emails with EmailHelper.parseEmail when writing token payloads
- Invalidate old-format tokens after schema changes
- Never hand-construct confirmation tokens outside the official send path
When it happens
Trigger: peekValueFromToken returned data but data.user_id is null/undefined, or data.email is malformed/ambiguous so it fails the self-consistency check email === parseEmail(email).
Common situations: Hand-crafted or tampered token values; old tokens written by a previous schema version storing fields under different names; email stored with mixed case/whitespace by an older send path.
Related errors
- no token found
- cipherLabel cannot be empty
- cipherLabel must not contain a colon (:), got ${cipherLabel}
- cipherLabel must contain version suffix (e.g. 2042.1-v42), g
- cipherPasswords['${cipherLabel}'] is too short
AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03).
Data as JSON: /api/errors/5fefffba4426b6f0.
Report an issue: GitHub.