Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions langfuse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
LangfuseTool,
)
from ._version import __version__
from ._utils.request import LangfuseAuthException
from .span_filter import (
KNOWN_LLM_INSTRUMENTATION_SCOPE_PREFIXES,
is_default_export_span,
Expand Down Expand Up @@ -66,6 +67,7 @@
"RunnerContext",
"RegressionError",
"__version__",
"LangfuseAuthException",
"is_default_export_span",
"is_langfuse_span",
"is_genai_span",
Expand Down
22 changes: 20 additions & 2 deletions langfuse/_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@
from langfuse._utils.environment import get_common_release_envs
from langfuse._utils.parse_error import handle_fern_exception
from langfuse._utils.prompt_cache import PromptCache
from langfuse._utils.request import LangfuseAuthException
from langfuse.api import (
AccessDeniedError,
CreateChatPromptRequest,
CreateChatPromptType,
CreateTextPromptRequest,
Expand All @@ -103,6 +105,7 @@
Prompt_Text,
ScoreBody,
TraceBody,
UnauthorizedError,
)
from langfuse.batch_evaluation import (
BatchEvaluationResult,
Expand Down Expand Up @@ -3166,7 +3169,10 @@ 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.
LangfuseAuthException: If authentication fails. This covers both the
"credentials are valid but no projects are accessible" case and
the "credentials are invalid" case (HTTP 401 Unauthorized /
HTTP 403 Forbidden returned by the Langfuse API).

Note:
This method is blocking. It is discouraged to use it in production code.
Expand All @@ -3177,7 +3183,7 @@ def auth_check(self) -> bool:
f"Auth check successful, found {len(projects.data)} projects"
)
if len(projects.data) == 0:
raise Exception(
raise LangfuseAuthException(
"Auth check failed, no project found for the keys provided."
)
return True
Expand All @@ -3188,6 +3194,18 @@ def auth_check(self) -> bool:
)
return False

except (UnauthorizedError, AccessDeniedError) as e:
# HTTP 401 / 403 from the Langfuse API: bad public/secret key or
# insufficient permissions. Re-raise as LangfuseAuthException so
# callers can catch all auth-failure modes with one except clause,
# not just the "zero projects" path above.
handle_fern_exception(e)
raise LangfuseAuthException(
f"Auth check failed: {type(e).__name__} returned by Langfuse API. "
"Verify your public/secret key and host. See "
"https://langfuse.com/docs/sdk/python#installation-and-setup."
) from e

except Error as e:
handle_fern_exception(e)
raise e
Expand Down
24 changes: 24 additions & 0 deletions langfuse/_utils/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,27 @@ def __str__(self) -> str:
errors = ", ".join(str(error) for error in self.errors)

return f"[Langfuse] {errors}"


class LangfuseAuthException(Exception):
"""Raised when Langfuse authentication fails.

This exception is raised when the provided credentials (public key and
secret key) fail authentication. This covers both invalid credentials
(HTTP 401 Unauthorized / HTTP 403 Forbidden) and the case where valid
credentials have no accessible projects. Catching this specific exception
type allows callers to distinguish authentication failures from other
errors and handle them explicitly.

Example::

from langfuse import Langfuse, LangfuseAuthException

try:
Langfuse().auth_check()
except LangfuseAuthException as e:
# Handle auth failure cleanly without masking unrelated bugs
logger.warning("Langfuse auth failed: %s", e)
"""

pass