fix(vector_stores): name the connection string when Atlas rejects MongoDB credentials

Atlas answers a wrong password with code 8000 "AtlasError" rather than the 18 a
self-hosted deployment returns, so the code-only check never fired and a bad
password came back as a generic "MongoDB rejected the vector search", pointing
the reader at the index instead of at their credentials. Verified live against
Atlas with a tampered password.
This commit is contained in:
Yuneng Jiang 2026-09-02 10:03:23 -07:00
parent 8374b34b81
commit 85431297b9
No known key found for this signature in database
2 changed files with 34 additions and 2 deletions

View file

@ -110,6 +110,9 @@ def reset_client_cache() -> None:
_AUTHENTICATION_FAILED_CODE: Final = 18
_UNAUTHORIZED_CODE: Final = 13
# Atlas reports a rejected user as code 8000 "AtlasError" rather than 18, so the
# message is the only reliable signal for a serverless or shared-tier deployment.
_AUTHENTICATION_MESSAGE_MARKERS: Final = ("bad auth", "authentication failed", "not authorized")
def _index_hint(index_name: str, database: str, collection: str) -> str:
@ -169,12 +172,14 @@ def translate_mongo_error(error: Exception, index_name: str, database: str, coll
)
if isinstance(error, OperationFailure):
code: Final = error.code
if code in (_AUTHENTICATION_FAILED_CODE, _UNAUTHORIZED_CODE):
detail: Final = str(error).lower()
if code in (_AUTHENTICATION_FAILED_CODE, _UNAUTHORIZED_CODE) or any(
marker in detail for marker in _AUTHENTICATION_MESSAGE_MARKERS
):
return config_error(
"MongoDB rejected the credentials in mongodb_connection_string, or the database user "
f"lacks read access to '{database}.{collection}'. Driver detail: {error.details}"
)
detail: Final = str(error).lower()
if "dimension" in detail:
return config_error(
"The query embedding does not match the vector dimensions the Atlas index was built for. "

View file

@ -860,3 +860,30 @@ class TestErrorsCarryTheRightHttpStatus:
translate_mongo_error(original, index_name="idx", database="db", collection="coll")
is original
)
def test_atlas_rejected_credentials_are_named_even_though_the_code_is_8000():
"""Atlas answers a wrong password with code 8000 "AtlasError", not the 18 that a
self-hosted deployment returns, so a code-only check reports it as a generic
rejected search and never tells the caller to look at their connection string."""
from pymongo.errors import OperationFailure
error = OperationFailure(
"bad auth : authentication failed",
code=8000,
details={"ok": 0, "errmsg": "bad auth : authentication failed", "code": 8000, "codeName": "AtlasError"},
)
translated = translate_mongo_error(error, index_name="idx", database="sample_mflix", collection="embedded_movies")
assert isinstance(translated, BadRequestError)
assert "mongodb_connection_string" in str(translated)
assert "sample_mflix.embedded_movies" in str(translated)
def test_a_rejected_search_that_is_not_an_auth_failure_keeps_the_generic_message():
from pymongo.errors import OperationFailure
error = OperationFailure("PlanExecutor error", code=8, details={"errmsg": "PlanExecutor error"})
translated = translate_mongo_error(error, index_name="idx", database="db", collection="coll")
assert "mongodb_connection_string" not in str(translated)