diff --git a/langfuse/__init__.py b/langfuse/__init__.py index 6f2bfbb08..ce34f24f9 100644 --- a/langfuse/__init__.py +++ b/langfuse/__init__.py @@ -96,9 +96,11 @@ ) Langfuse = _client_module.Langfuse +LangfuseAuthCheckError = _client_module.LangfuseAuthCheckError __all__ = [ "Langfuse", + "LangfuseAuthCheckError", "LangfuseMedia", "LangfuseMediaReference", "get_client", diff --git a/langfuse/_client/client.py b/langfuse/_client/client.py index d3f699399..3e65d168b 100644 --- a/langfuse/_client/client.py +++ b/langfuse/_client/client.py @@ -176,6 +176,10 @@ def _serialize_evaluations(evaluations: List[Evaluation]) -> List[Dict[str, Any] ] +class LangfuseAuthCheckError(Exception): + """Raised by `Langfuse.auth_check()` when no project is found for the provided credentials.""" + + class Langfuse: """Main client for Langfuse tracing and platform features. @@ -3474,7 +3478,7 @@ def auth_check(self) -> bool: """Check if the provided credentials (public and secret key) are valid. Raises: - Exception: If no projects were found for the provided credentials. + LangfuseAuthCheckError: If no projects were found for the provided credentials. Note: This method is blocking. It is discouraged to use it in production code. @@ -3485,9 +3489,10 @@ def auth_check(self) -> bool: f"Auth check successful, found {len(projects.data)} projects" ) if len(projects.data) == 0: - raise Exception( + no_project_message = ( "Auth check failed, no project found for the keys provided." ) + raise LangfuseAuthCheckError(no_project_message) return True except AttributeError as e: diff --git a/tests/unit/test_auth_check.py b/tests/unit/test_auth_check.py new file mode 100644 index 000000000..9b2be34cb --- /dev/null +++ b/tests/unit/test_auth_check.py @@ -0,0 +1,39 @@ +"""Tests for Langfuse.auth_check() error behavior. + +See https://github.com/langfuse/langfuse/issues/906 - auth_check() used to +raise a bare `Exception` when no project was found for the provided +credentials, which is easy to swallow with a broad `except Exception` at the +call site. +""" + +from unittest.mock import Mock + +import pytest + +from langfuse import Langfuse, LangfuseAuthCheckError +from langfuse._client.resource_manager import LangfuseResourceManager + + +@pytest.fixture +def langfuse(): + langfuse_instance = Langfuse() + + if langfuse_instance._resources is None: + langfuse_instance._resources = Mock(spec=LangfuseResourceManager) + + langfuse_instance.api = Mock() + + return langfuse_instance + + +def test_auth_check_raises_langfuse_auth_check_error_when_no_projects(langfuse): + langfuse.api.projects.get.return_value = Mock(data=[]) + + with pytest.raises(LangfuseAuthCheckError): + langfuse.auth_check() + + +def test_auth_check_returns_true_when_projects_found(langfuse): + langfuse.api.projects.get.return_value = Mock(data=[Mock()]) + + assert langfuse.auth_check() is True