Skip to content

feat(python-sdk): introduce LangfuseAuthException for auth_check failures#1650

Open
Ashut0sh-mishra wants to merge 3 commits into
langfuse:mainfrom
Ashut0sh-mishra:fix/langfuse-auth-exception
Open

feat(python-sdk): introduce LangfuseAuthException for auth_check failures#1650
Ashut0sh-mishra wants to merge 3 commits into
langfuse:mainfrom
Ashut0sh-mishra:fix/langfuse-auth-exception

Conversation

@Ashut0sh-mishra
Copy link
Copy Markdown

@Ashut0sh-mishra Ashut0sh-mishra commented May 12, 2026

Summary

Closes #906 (filed in langfuse/langfuse).

Bare Exception raises are bad practice in Python because catching them also masks unrelated bugs (ImportError, NameError, SyntaxError, AttributeError, etc.). This is particularly problematic in a pattern like:

\\python
try:
langfuse.auth_check()
except Exception as e:
if is_non_prod:
logger.info('langfuse not started')
# silently continues even if e is a SyntaxError in your code!
\\

Changes

  • langfuse/_utils/request.py: Added LangfuseAuthException(Exception) — a dedicated, importable exception class for authentication failures, with a docstring and usage example.
  • langfuse/_client/client.py: Replaced
    aise Exception(...) in �uth_check() with
    aise LangfuseAuthException(...) and updated the docstring.
  • langfuse/init.py: Exported LangfuseAuthException from the top-level package.

After this change

Users can catch auth failures precisely without masking other bugs:

\\python
from langfuse import Langfuse, LangfuseAuthException

def initialize():
Langfuse().auth_check() # any typo here now raises the appropriate error

try:
initialize()
except LangfuseAuthException as e:
if environment.is_non_prod:
logger.info('Langfuse not started: %s', e)
# SyntaxError, NameError, etc. will still propagate and fail loudly
\\

Checklist

  • Feature / improvement
  • Backward compatible (existing code catching Exception still works; LangfuseAuthException is a subclass of Exception)
  • New public symbol exported from langfuse package

Greptile Summary

This PR introduces LangfuseAuthException, a dedicated exception subclass of Exception, to give callers a precise type to catch when auth_check() fails, rather than relying on bare Exception. The exception is defined in langfuse/_utils/request.py, used in auth_check(), and exported from the top-level langfuse package.

  • LangfuseAuthException is only raised for the "valid credentials, zero projects" code path. Invalid API keys produce an HTTP 401, which is caught by the except Error branch and re-raised as a Fern Error — not as LangfuseAuthException — so except LangfuseAuthException will silently miss the most common auth-failure scenario.
  • The docstring in LangfuseAuthException acknowledges this scope ("credentials are valid but no projects are accessible"), but the exception's name and its placement in the public API imply broader coverage, which is likely to confuse users.

Confidence Score: 3/5

Safe to merge if the narrow scope of LangfuseAuthException (empty-projects path only) is the intended design; risky if the goal is a single catch-all type for every auth failure.

The new exception only fires in one of three failure branches inside auth_check(). A caller with bad API keys will receive an unwrapped Fern Error, not LangfuseAuthException, so users who adopt the new type expecting full coverage will still see unhandled exceptions in the most common failure scenario.

langfuse/_client/client.py — specifically the except Error re-raise in auth_check() at line 3192–3194, which bypasses the new exception type.

Sequence Diagram

sequenceDiagram
    participant User
    participant Langfuse
    participant API as projects.get()

    User->>Langfuse: auth_check()
    Langfuse->>API: GET /projects

    alt HTTP 401 / bad credentials
        API-->>Langfuse: Fern Error (401)
        Langfuse->>Langfuse: except Error → handle_fern_exception(e)
        Langfuse-->>User: raises Fern Error (NOT LangfuseAuthException)
    end

    alt Valid credentials, empty project list
        API-->>Langfuse: "200 OK, data=[]"
        Langfuse-->>User: raises LangfuseAuthException ✓
    end

    alt Valid credentials, project found
        API-->>Langfuse: "200 OK, data=[project]"
        Langfuse-->>User: returns True
    end

    alt Client not initialized (AttributeError)
        Langfuse->>Langfuse: except AttributeError → log warning
        Langfuse-->>User: returns False
    end
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
langfuse/_client/client.py:3192-3194
**`LangfuseAuthException` not raised on HTTP 401/403 (invalid credentials)**

The exception is only raised when the API call succeeds but returns an empty projects list. When a caller provides completely wrong API keys, `self.api.projects.get()` will throw a Fern `Error` (e.g. HTTP 401), which is caught by the `except Error` branch and re-raised as-is — **not** as `LangfuseAuthException`. A user who writes `except LangfuseAuthException` to catch all auth failures will silently miss the most common failure case (bad credentials), defeating the purpose of the new exception type.

Reviews (1): Last reviewed commit: "feat(python-sdk): introduce LangfuseAuth..." | Re-trigger Greptile

…ures

Bare Exception raises are bad practice in Python because they cannot be
cleanly caught without also masking unrelated bugs (ImportError, NameError,
SyntaxError, etc.).

This commit introduces LangfuseAuthException(Exception), a dedicated
exception class for authentication failures, and replaces the bare
raise Exception(...) in Langfuse.auth_check() with it.

Users can now guard specifically against auth failures:

    from langfuse import Langfuse, LangfuseAuthException

    try:
        Langfuse().auth_check()
    except LangfuseAuthException as e:
        logger.warning('Langfuse auth failed: %s', e)

LangfuseAuthException is exported from the top-level langfuse package.

Closes langfuse/langfuse#906

Co-authored-by: nik464 <nik464@users.noreply.github.com>
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 12, 2026

CLA assistant check
All committers have signed the CLA.

Addresses greptile review on langfuse#1650: previously LangfuseAuthException was
only raised on the "valid credentials, zero projects" path. Invalid API
keys produce HTTP 401 / 403, which fell through to the generic `except
Error` branch and re-raised the underlying Fern error -- so `except
LangfuseAuthException` silently missed the most common auth-failure
scenario.

Now catch `UnauthorizedError` (401) and `AccessDeniedError` (403)
explicitly and re-raise them as `LangfuseAuthException` with the
original exception chained via `raise ... from e`. The docstring is
updated so the exception's documented scope matches its name.

Co-authored-by: nik464 <nik464@users.noreply.github.com>
@Ashut0sh-mishra
Copy link
Copy Markdown
Author

Thanks @greptile-apps! You're right -- the original scope was too narrow. Updated auth_check to also catch UnauthorizedError (401) and AccessDeniedError (403) from the Fern API and re-raise them as LangfuseAuthException (with the original exception chained via raise ... from e). The docstring is updated so the documented scope matches the exception's name. Pushed.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 12, 2026

The fix looks good — UnauthorizedError and AccessDeniedError are now caught before the generic except Error branch and re-raised as LangfuseAuthException with proper exception chaining. The ordering is correct (specific before generic).

One small follow-up: the class docstring in langfuse/_utils/request.py still describes only the old narrow scope:

"credentials are valid but no projects are accessible with them"

Since the auth_check docstring was updated, the class docstring should match. Suggested fix:

    """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)
    """

Everything else looks solid — the exception ordering, the from e chaining, and the updated auth_check docstring are all correct.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

Co-authored-by: nik464 <nik464@users.noreply.github.com>
@Ashut0sh-mishra
Copy link
Copy Markdown
Author

Good catch @greptile-apps -- updated the class docstring in langfuse/_utils/request.py to match the widened auth_check scope (now mentions HTTP 401 / 403 alongside the no-accessible-projects case). Pushed in c6825eb.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 12, 2026

The full PR diff shows this was already handled before c6825ebclient.py has a new except (UnauthorizedError, AccessDeniedError) block that catches 401/403 and re-raises as LangfuseAuthException:

except (UnauthorizedError, AccessDeniedError) as e:
    handle_fern_exception(e)
    raise LangfuseAuthException(
        f"Auth check failed: {type(e).__name__} returned by Langfuse API. ..."
    ) from e

That block is correctly ordered before the generic except Error fallthrough, so 401/403 responses are now wrapped in LangfuseAuthException rather than leaking as bare Fern errors. The c6825eb docstring update accurately reflects the actual behavior now. The original concern is fully addressed — this LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants