Mintplex-Labs/anything-llm · error

User account suspended.

Error message

User account suspended.

What it means

validate() found a valid, unexpired token, but the linked user record has suspended=true, so authentication is refused and no session JWT is minted. This is an account-state rejection, not a token problem.

Source

Thrown at server/models/temporaryAuthToken.js:84

   * @param {string} publicToken - the token to validate against
   * @returns {Promise<{sessionToken: string|null, token: import("@prisma/client").temporary_auth_tokens & {user: import("@prisma/client").users} | null, error: string | null}>}
   */
  validate: async function (publicToken = "") {
    /** @type {import("@prisma/client").temporary_auth_tokens & {user: import("@prisma/client").users} | undefined | null} **/
    let token;

    try {
      if (!publicToken)
        throw new Error(
          "Public token is required to validate a temporary auth token."
        );
      token = await prisma.temporary_auth_tokens.findUnique({
        where: { token: String(publicToken) },
        include: { user: true },
      });
      if (!token) throw new Error("Invalid token.");
      if (token.expiresAt < new Date()) throw new Error("Token expired.");
      if (token.user.suspended) throw new Error("User account suspended.");

      // Create a new session token for the user valid for 30 days
      const sessionToken = makeJWT(
        { id: token.user.id, username: token.user.username },
        process.env.JWT_EXPIRY
      );

      return { sessionToken, token, error: null };
    } catch (error) {
      console.error("FAILED TO VALIDATE TEMPORARY AUTH TOKEN.", error.message);
      return { sessionToken: null, token: null, error: error.message };
    } finally {
      // Delete the token after it has been used under all circumstances if it was retrieved
      if (token)
        await prisma.temporary_auth_tokens.delete({ where: { id: token.id } });
    }
  },
};

View on GitHub (pinned to 3aec848f28)

Solutions

  1. An administrator must unsuspend the user (users.suspended = false) before this login path can work
  2. If suspension is intentional, present an 'account suspended' message and stop - do not retry
  3. Use a different, active account if the suspended one is not yours

Example fix

// before
// retrying validation repeatedly after suspension
for (let i = 0; i < 3; i++) await TemporaryAuthToken.validate(publicToken);

// after
const { sessionToken, error } = await TemporaryAuthToken.validate(publicToken);
if (error === 'User account suspended.') {
  return res.status(403).send('Account suspended. Contact your administrator.');
}
Defensive patterns

Strategy: try-catch

Validate before calling

null // suspension state is authoritative server-side; pre-checking it would race with admin actions

Try / catch

const { sessionToken, error } = await TemporaryAuthToken.validate(publicToken);
if (error === 'User account suspended.') {
  // account-state failure: respond 403 and stop - retrying cannot succeed
  return res.status(403).json({ error: 'Account suspended. Contact your administrator.' });
}

Prevention

When it happens

Trigger: An admin suspended the user after the token was created; a previously suspended user retries an old magic link; bulk moderation actions suspending accounts while login links were outstanding.

Common situations: Abuse-response workflows suspending accounts mid-login; offboarded employees clicking cached login links; shared inboxes where a suspended account's link is reused by someone else.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/f52064a3aed681c6. Report an issue: GitHub.