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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions