Skip to content

bug: Client.init() silently succeeds with invalid API key — async auth swallows exceptions #1336

@CrepuscularIRIS

Description

@CrepuscularIRIS

Bug Description

Client.init(api_key="invalid-key") silently succeeds instead of raising InvalidApiKeyException or ApiServerException. The async auth refactor moved authentication into a background daemon thread (_start_auth_task), so auth errors are swallowed and never propagated to the caller. Users get no feedback when their API key is wrong — they only discover the problem later when span exports silently fail with 401 errors.

Reproduction

git clone --depth=1 https://github.com/AgentOps-AI/agentops.git
cd agentops
uv venv .venv && source .venv/bin/activate
uv pip install -e . pytest pytest-asyncio pytest-mock pytest-recording vcrpy requests-mock openai anthropic "openai-agents[voice]"
pytest tests/integration/test_auth_flow.py::test_auth_flow_invalid_key -v --timeout=60 -p no:depends

Stack Trace

FAILED tests/integration/test_auth_flow.py::test_auth_flow_invalid_key - Failed: DID NOT RAISE any of
  (<class 'agentops.exceptions.InvalidApiKeyException'>, <class 'agentops.exceptions.ApiServerException'>)

tests/integration/test_auth_flow.py:49: Failed
    with pytest.raises((InvalidApiKeyException, ApiServerException)) as exc_info:
E   Failed: DID NOT RAISE any of (...)

Root Cause

agentops/client/client.py:118-145_start_auth_task() runs _fetch_auth_async(api_key) in a daemon thread via asyncio.run(). When _fetch_auth_async raises ApiServerException, the exception is caught inside the thread and never propagated back to init().

At line 200, init() calls self._start_auth_task(self.config.api_key) and immediately proceeds, marking self._initialized = True at line 246 regardless of auth outcome.

Compare with the working test test_auth_flow which doesn't check for errors — it only verifies the happy path.

Impact

  • Users with invalid/expired API keys get silently initialized clients
  • No error feedback at init time — auth failures only surface as 401 export errors later
  • Breaks the expected contract that init() validates the API key

Suggested Fix

Make the first auth attempt synchronous in init(), then switch to async for token refresh:

# In init(), replace _start_auth_task with synchronous first attempt:
if self.config.api_key:
    try:
        import asyncio
        response = asyncio.run(self._fetch_auth_async(self.config.api_key))
        if not response:
            raise InvalidApiKeyException("Authentication failed")
    except ApiServerException:
        raise

Found by running the test suite. Happy to submit a PR if confirmed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions