Skip to content

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

Open

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.

Activity

  1. 0xbrainkid commented on Apr 15, 2026

    @0xbrainkid

    Silent success on an invalid API key is a particularly dangerous failure mode for agentic deployments — the agent initializes, runs sessions, and silently drops all telemetry. Operators only discover the problem when they notice the dashboard is empty, which in a long-running autonomous agent might be hours or days later.

    The root cause you identified (auth in a daemon thread with swallowed exceptions) is a common async initialization anti-pattern. Two fixes worth combining:

    1. Synchronous auth probe before returning from init(). A lightweight synchronous preflight request (even just a token validation endpoint) at init time guarantees the key is valid before the call returns. The daemon thread can still handle refresh and rotation; the synchronous probe just confirms the initial credential is valid.

    2. Identity invalidation event on deferred auth failure. If the daemon thread auth does fail after init returns (e.g., token revoked mid-session), the SDK should emit an identity invalidation event that upstream agents and orchestrators can intercept. An agent that loses its identity silently mid-session is a trust boundary violation — downstream tools and collaborating agents may have made decisions based on the assumption the agent was authenticated.

    From a behavioral trust perspective, an agent that initializes with an invalid API key and then silently fails to report telemetry has a broken attestation chain — its actions cannot be attributed or audited. AgentFolio and SATP track observable telemetry as a trust signal; silent initialization failure creates a gap in that record that looks identical to an agent that simply had no activity.

  2. HhH666-ux commented on Nov 6, 2026

    @HhH666-ux

    I reproduced this on main with the reported test (tests/integration/test_auth_flow.py::test_auth_flow_invalid_key): init() returned successfully and the ApiServerException never reached the caller.

    The fix keeps the background task for async callers, but when init() runs without a running event loop (the synchronous API), it now authenticates before returning and lets InvalidApiKeyException / ApiServerException propagate. Other unexpected errors are still swallowed as before, and the happy path is unchanged.

    Both tests in tests/integration/test_auth_flow.py pass locally. Opening a PR that links this issue.

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

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