Meteor Accounts.createUser - Meteor crashes when user exists

I’m new to Meteor and trying to figure out, how Account.createUser() works when the user is in the database already and meteor checks for duplicate entries.

I’ve created a Meteor method:

'test.createUser'(user) {
try {
  Accounts.createUser(
    {
      username: user.username,
      email: user.emails[0].address,
      profile: user.profile,
    },
  );
} catch (exception) {
  console.log('Error creating user:', exception);
  throw exception;
}

},

Which I call on the client side:

createUser() {
  Meteor.call(
    "test.createUser",
    {
      username: "myuser",
      emails: [{ address: "myuser@example.com", verified: true }],
      profile: {
        profile: "userProfile",
        name: "New User",
        path: null,
      },
    },
    (error) => {
      if (error) {
        console.error("Error:", error);
      }
    }
  );
},

If the user did not exist in mongo and test.createUser is called the 1st time, the user is created. On subsequent calls, I’d expect that the catch-block of test.createUser runs, as the username and email are in use already.

However, this does not happen. Instead, Meteor crashes with the error displayed on the console:

W20241231-09:01:11.046(1)? (STDERR) meteor://:computer:app/packages/accounts-base.js:685
W20241231-09:01:11.046(1)? (STDERR) const error = new Meteor.Error(errorCode, isErrorAmbiguous ? ‘Something went wrong. Please check your credentials.’ : msg);
W20241231-09:01:11.046(1)? (STDERR) ^
W20241231-09:01:11.067(1)? (STDERR) errorClass [Error]: Something went wrong. Please check your credentials. [403]
W20241231-09:01:11.067(1)? (STDERR) at AccountsServer._handleError (packages/accounts-base/accounts_server.js:1523:19)
W20241231-09:01:11.068(1)? (STDERR) at AccountsServer._checkForCaseInsensitiveDuplicates (packages/accounts-base/accounts_server.js:1491:14)
W20241231-09:01:11.068(1)? (STDERR) at processTicksAndRejections (node:internal/process/task_queues:105:5)
W20241231-09:01:11.068(1)? (STDERR) at AccountsServer._createUserCheckingDuplicates (packages/accounts-base/accounts_server.js:1504:5)
W20241231-09:01:11.068(1)? (STDERR) at AccountsServer.createUser (packages/accounts-password/password_server.js:996:12) {
W20241231-09:01:11.068(1)? (STDERR) isClientSafe: true,
W20241231-09:01:11.068(1)? (STDERR) error: 403,
W20241231-09:01:11.068(1)? (STDERR) reason: ‘Something went wrong. Please check your credentials.’,
W20241231-09:01:11.068(1)? (STDERR) details: undefined,
W20241231-09:01:11.068(1)? (STDERR) errorType: ‘Meteor.Error’
W20241231-09:01:11.068(1)? (STDERR) }
W20241231-09:01:11.068(1)? (STDERR)
W20241231-09:01:11.068(1)? (STDERR) Node.js v22.11.0

Catching the exception and re-throw it does not work.

I’ve also tried to call Accunts.createUser on the client side:

createUser2() {
  const user = {
      username: "myuser",
      emails: [{ address: "myuser@example.com", verified: true }],
      password: "secret",
      profile: {
        profile: "userProfile",
        name: "New User",
        path: null,
      },
    },
  };
  try {
    Accounts.createUser(
      {
        username: user.username,
        email: user.emails[0].address,
        password: user.password,
        profile: user.profile,
      },
      (err) => {
        console.log("we got an error:", err);
      }
    );
  } catch (exception) {
    console.log("Error creating user:", exception);
    throw exception;
  }
},

},

we got an error:
{
“isClientSafe”: true,
“error”: 403,
“reason”: “Something went wrong. Please check your credentials.”,
“message”: “Something went wrong. Please check your credentials. [403]”,
“errorType”: “Meteor.Error”
}

In client mode, the callback containing the error runs.

But how can I catch it when using the Meteor method and avoid crashing Meteor?

You have (from your log):
AccountsServer._createUserCheckingDuplicates and AccountsServer._checkForCaseInsensitiveDuplicates
These are in charge with testing the uniqueness of a user.

(error) => {
      if (error) {
        console.error("Error:", error);
      }
    }

I think you don’t need an if here because that is running anyway only if an error is occurring.

The errors you are getting are not what the documentation describes so this might need to be looked and and retested by the Meteor team.

Also the user creation might not require a try/catch since it already supports a callback.

On the Meteor website you have a feature “Ask AI”. I highlighted below the main recommendation for this workflow but nevertheless, the error you are getting is not right.:

If you try to create a user that already exists in Meteor, the Accounts.createUser method will throw an error. Here’s what happens in different scenarios:

  1. If the username already exists:
    The error message will be “Username already exists.”

  2. If the email already exists:
    The error message will be “Email already exists.”

This behavior is described in the Accounts.createUser API documentation:

“On the client, you must pass password and at least one of username or email — enough information for the user to be able to log in again later. If there are existing users with a username or email only differing in case, createUser will fail. The callback’s error.reason will be 'Username already exists.' or 'Email already exists.'

To handle this situation in your code, you should check for these errors when calling Accounts.createUser. Here’s an example of how you might do this:

Accounts.createUser({
  username: "exampleUser",
  email: "user@example.com",
  password: "password123"
}, (error) => {
  if (error) {
    if (error.reason === "Username already exists.") {
      console.log("This username is already taken");
    } else if (error.reason === "Email already exists.") {
      console.log("This email is already registered");
    } else {
      console.log("Error creating user:", error.reason);
    }
  } else {
    console.log("User created successfully");
  }
});

In case of an existing email, the documentation suggests: “In the latter case, the user can then either login or reset their password.”

:point_right: :point_right: It’s a good practice to check if a user exists before attempting to create a new one, especially on the server side. You can use methods like Accounts.findUserByUsername or Accounts.findUserByEmail for this purpose.

Remember, these operations should be handled carefully to avoid exposing sensitive information about existing users.

Hi paulishca,

thanks for the reply.
Regarding the callback, you’ re right. But this is for client mode only.
The issue arises when I’m calling Accounts.createUser in a method on the server side. There is no callback in this case. And no password needed.

Accounts.createUser documentation

It’s totally fine that Meteor throws an error in case the user does already exist.
But I should be able to handle it inside try - catch. The problem is that this doesn’t work.
Meteor just logs the error to the console (not because of the catch-block) and crashes.
The only way to avoid that was using

process.on('unhandledRejection', (reason) => {
  console.error('Unhandled Rejection:', reason);
});

globally. Which isn’t nice at all.

However, I’ve tried the AI on the Meteor side you mentioned, and got the cause for the issue:

Yes, you can run Accounts.createUser on the server side as a method. However, it’s important to note that in Meteor 3.0, this method has been updated to be asynchronous.

Here’s how you can use it in a server-side method:

Meteor.methods({
  async createUserAccount(options) {
    try {
      const userId = await Accounts.createUserAsync(options);
      return userId;
    } catch (error) {
      throw new Meteor.Error('user-creation-failed', error.reason);
    }
  }
});

Note the following important points:

  1. The method is defined as async.

  2. We use Accounts.createUserAsync instead of Accounts.createUser.

  3. We use await when calling the method.

  4. It’s a good practice to wrap the call in a try-catch block to handle any errors.

And that works-)

There is a page describing the change to async when migrating from Meteor v2 to v3. Unfortunately, the v3 documentation still needs to be updated.

Thanks a lot for the hint. -)

1 Like

Accounts.validateNewUser()

Can’t you just check if the user exists before creating?

I do like this in a method:

  const emailAlreadyTaken = await Meteor.users.findOneAsync({
    'emails.address': payload.email,
  })

  if (emailAlreadyTaken) {
    throw new Meteor.Error(
      'INPUT_VALIDATION_ERROR',
      'Validation failed',
      JSON.stringify([{ field: 'email', message: 'E-mail em uso.' }]),
    )
  }

  const userId = await Accounts.createUserAsync({
    profile: {
      name: payload.name,
      newlyCreated: true,
      role: Number(payload.role),
    },
    email: payload.email,
    password: Meteor.settings.DEFAULT_NEW_USER_PASSWORD,
  })