The MongoDB login failure that is not a wrong password
authSource names the database the credentials live in, not the one you are opening, and getting it wrong fails exactly like a bad password.
A user is created inside a database. That single sentence is the reason most
first-connection attempts against this engine end in an AuthenticationError
reading Authentication failed, and the reason authSource is the field to
check before you retype the password. The driver checks credentials against whichever database the
connection names, unless something tells it otherwise. In the ordinary
deployment - users administered in admin, application data in a database of
its own - nothing tells it otherwise, and the server answers with the same error
it would give a typo.
The four things a MongoDB connection needs
LibreDB Studio speaks MongoDB through the official mongodb Node.js driver
(node-mongodb-native) on the default port 27017. validate() accepts a
connection in one of two shapes: a connection string, or discrete fields with at
least a host and a database. From the discrete fields the provider assembles the
URI itself:
mongodb://<user>:<password>@<host>:<port>/<database>[?authSource=<authSource>]
Four separable facts go into that line, and only three of them are the ones people think about.
| Field | What it answers |
|---|---|
| host, port | Where the server is. Port defaults to 27017. |
| database | Which database you are opening. |
| user, password | Who you are. The pair is URL-encoded, and the whole user:password@ segment is dropped when neither is set. |
| authSource | Which database holds the record of who you are. |
The fourth is the one that is not a restatement of the second. If it is empty, no query string is appended, and the driver falls back to the database named in the path. That fallback is correct for exactly one deployment shape: the user was created in the database being opened.
Why authSource is separate from the database you open
MongoDB creates users inside a database. A user created in admin and a user
of the same name created in shop are two different principals. Authentication
is therefore a
two-argument operation - credentials, plus the database to check them against -
and the connection URI only carries one database in an obvious place.
authSource is the second argument. It is not a permission setting, not a
default schema, and not a fallback for the database field. It says: look for
this user here, then open that.
// user created in the database being opened - no authSource needed
{ host: 'localhost', port: 27017, database: 'app', user: 'app', password: 'secret' }
// user created in admin, data in shop - the ordinary deployment
{ host: 'localhost', port: 27017, database: 'shop',
user: 'app', password: 'secret', authSource: 'admin' }
The second form could not be expressed through the discrete fields at all before the field existed, which is how we learned what the failure looks like from the outside.
Authentication failed, and why that message misleads
The provider maps driver errors through the shared mapDatabaseError(), with no
MongoDB-specific branches. A driver message containing authentication becomes
an AuthenticationError, reported as Authentication failed: followed by the
driver’s own text. A failure to reach the server at all becomes a
ConnectionError that carries the host and port. Those are two different
classes, and the split is the useful part - but a wrong authSource lands in
the first one, because from the server’s point of view nothing is wrong with the
network and no such user exists in the database it was asked to look in.
So the error is accurate and unhelpful at the same time. It tells you
authentication failed. It does not tell you that authentication was attempted
against shop when the user lives in admin. Two causes arrive wearing the
same sentence:
- the password is genuinely wrong;
- the user exists, in a different database, and
authSourceis empty or wrong.
Retyping the password addresses the first and rules out nothing else. Checking where the user was created costs one command and eliminates the second.
A third failure gets blamed on the credentials without ever producing that
message. A server started with --tlsMode requireTLS refuses a connection whose
SSL mode is disable, and says so in its own words - The server is configured
to only allow SSL connections, logged in a measurement taken on 2026-08-23
against mongo:latest. Nothing in that string is matched as an authentication
failure, so it is not classified as one. Read the message before you decide
which failure you are holding.
Pasted URIs carry their own auth source
A connectionString is used verbatim. buildConnectionString() returns it
untouched if present, and never appends to it - so a pasted URI carries its own
?authSource=, and the connection form offers no separate input while you are
in that mode. This is deliberate: two copies of the same parameter, one in
the string and one in a field beside it, is one value with two places to
disagree with itself.
The consequence for debugging is that the field you were told to check is not missing, it is inside the string you pasted. If the URI came from a platform console or a teammate’s clipboard, read the query string before you read anything else, because nothing the form does will edit it.
TLS is the exception to that rule, and it is worth knowing which way it cuts.
The SSL panel does apply alongside a pasted string, because the driver reads a
second options channel it prefers over the URI. A tls=true or ssl=true in
the URI sets the form to verify-system, and a mongodb+srv:// scheme with no
TLS parameter does the same, because SRV implies TLS in the driver itself.
authSource does not work that way. The string wins, entirely.
Confirming the connection before you blame the credentials
The cheapest way to tell those failures apart is to open a connection that has none of them. Start a server with no authentication and no TLS, and connect to it:
docker run --rm -p 27017:27017 mongo:7
Then open mongodb://localhost:27017/test in the connection dialog. After the
client is built, the provider issues a { ping: 1 } command before reporting
success, so a connection that lands in the tree has actually spoken to the
server rather than merely resolved its address. If that works and your real
connection does not, the transport is fine and you are looking at credentials or
at where they live. The repo fixture, the mongodb service in
database-compose.yml, is that shape: a root user admin with password
admin, created in admin, which is therefore the auth source whichever
database you open.
Once you are in, there is a boundary to know about before you type anything into
the editor. No SQL translation layer is faked here: the editor runs MongoDB
JSON command objects, and a statement written in shell syntax starting with
db. cannot be run at all. Not translated, not approximated - the parser
requires a document with collection and operation, and anything else is a
query error that answers with the format it expected.
{ "collection": "users", "operation": "find", "filter": { "active": true } }
That is the same envelope the agent’s plan mode is given verbatim - the JSON
command shape, and the explicit note that a statement starting with db. cannot
be run here - because a plan run on 2026-08-22 drafted
db.orders.aggregate([...]), correct MongoDB that the editor cannot execute.
Plan mode is the only agent mode this connection has. It is toolless, it runs
nothing, and it drafts a statement for a person to run; the metered auto run ends
engine-unsupported here, because the read-only execution profile it needs is
database-native and exists only on PostgreSQL, SQLite and DuckDB.
A find with no explicit limit returns at most 100 documents, so a first query
that looks suspiciously round is not a truncation bug. The collection list is
read with listCollections() and capped at 200, but the field types under each
collection are inferred from a 100-document sample rather than read from a
catalog, which is worth remembering the first time a field you know exists is
missing from the tree.
The engine’s published limits, transport and default port are listed on the databases page, and the connection dialog itself is the first thing covered in getting started.