Mintplex-Labs/anything-llm · error · Error
Failed to generate user recovery codes!
Error message
Failed to generate user recovery codes!
What it means
Thrown by generateRecoveryCodes after RecoveryCode.createMany succeeded but the subsequent User._update to set seen_recovery_codes=true returned a falsy success. Recovery codes were already persisted, yet the user row could not be marked, so the function aborts to avoid returning codes whose 'seen' flag is inconsistent. It indicates a user-level write failure, not a recovery-code write failure.
Source
Thrown at server/utils/PasswordRecovery/index.js:28
const newRecoveryCodes = [];
const plainTextCodes = [];
for (let i = 0; i < 4; i++) {
const code = v4();
const hashedCode = bcrypt.hashSync(code, 10);
newRecoveryCodes.push({
user_id: userId,
code_hash: hashedCode,
});
plainTextCodes.push(code);
}
const { error } = await RecoveryCode.createMany(newRecoveryCodes);
if (!!error) throw new Error(error);
const { user: success } = await User._update(userId, {
seen_recovery_codes: true,
});
if (!success) throw new Error("Failed to generate user recovery codes!");
return plainTextCodes;
}
async function recoverAccount(username = "", recoveryCodes = []) {
const user = await User.get({ username: String(username) });
if (!user) return { success: false, error: "Invalid recovery codes." };
// If hashes do not exist for a user
// because this is a user who has not logged out and back in since upgrade.
const allUserHashes = await RecoveryCode.hashesForUser(user.id);
if (allUserHashes.length < 4)
return { success: false, error: "Invalid recovery codes." };
const uniqueRecoveryCodes = [
...new Set(
recoveryCodes
.map((code) => (typeof code === "string" ? code.trim() : ""))View on GitHub (pinned to 526360e320)
Solutions
- Verify the userId passed to generateRecoveryCodes still exists in the users table at call time.
- Inspect DB logs/errors around the User._update call for the underlying failure.
- Treat this as a partial-write: the codes were created, so on retry ensure createMany is idempotent or clean up orphaned RecoveryCode rows for that user first.
- If the user was deleted intentionally, stop calling generateRecoveryCodes for that id.
Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the user exists and is writable before generating codes.
const user = await User.get({ id: userId });
if (!user) {
return { success: false, error: "User not found; cannot generate recovery codes." };
} Try / catch
try {
return await generateRecoveryCodes(userId);
} catch (e) {
if (/Failed to generate user recovery codes/i.test(e.message)) {
// Codes were persisted but the user flag was not — clean up to avoid orphan rows.
await RecoveryCode.deleteMany({ user_id: userId }).catch(() => {});
return { success: false, error: "Could not finalize recovery codes; please retry." };
}
throw e;
} Prevention
- Pass a fresh userId obtained from the authenticated session, not a cached value.
- Wrap code generation in a transaction or compensate on failure (delete the just-created codes).
- Log the underlying User._update failure for diagnosis.
When it happens
Trigger: The user record was deleted or its row locked between the two writes; the User model _update returned {user:false} due to a DB error or unknown user id; a concurrent session reset the user mid-flow.
Common situations: Account deletion racing with a login that triggers code generation; DB connection blip; passing a stale/invalid userId from a stale session token.
Related errors
- [Gitea Loader]: not in ready state!
- [GitHub Loader]: not in ready state!
- [Gitlab Loader]: not in ready state!
- Failed to update default system prompt.
- Run not found
AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13).
Data as JSON: /api/errors/620481d81bde9f7a.
Report an issue: GitHub.