NousResearch/hermes-agent · error · ValueError

no usable api_key / token provider

Error message

no usable api_key / token provider

What it means

materialize_bearer_for_http() rejects its input because it is neither a recognized zero-arg callable token provider nor a non-empty string. This is a programming/validation error in whatever assembled the auth value for the Anthropic-style Foundry client — not an environmental failure.

Source

Thrown at agent/azure_identity_adapter.py:475

    accept a ``Callable[[], str]`` for ``auth_token``. Instead,
    :func:`build_bearer_http_client` returns an ``httpx.Client`` whose
    request event hook calls this function and rewrites the
    ``Authorization`` header per request — and that client is passed to
    the Anthropic SDK via ``http_client=...``. See
    :func:`agent.anthropic_adapter.build_anthropic_client` for the
    consumer.

    Raises ``ValueError`` if ``value`` is not a callable token provider
    or non-empty string.
    """
    if is_token_provider(value):
        token = value()
        if not isinstance(token, str) or not token:
            raise ValueError("token provider returned empty value")
        return token
    if isinstance(value, str) and value:
        return value
    raise ValueError("no usable api_key / token provider")


def build_bearer_http_client(token_provider: Callable[[], str], **httpx_kwargs: Any) -> Any:
    """Return an ``httpx.Client`` that mints a fresh Entra bearer JWT
    per outbound request.

    The Anthropic SDK (≤ 0.86.0 at the time of writing) stores
    ``api_key`` / ``auth_token`` as static strings and computes the
    ``Authorization`` header at construction time. To get per-request
    token refresh (the Microsoft-recommended Foundry pattern for
    callable bearer providers), we install an httpx ``request`` event
    hook on a custom client and pass that client to the SDK via
    ``http_client=...``. The hook:

      1. Calls :func:`materialize_bearer_for_http` to mint a fresh JWT
         (azure-identity caches internally — this is cheap when the
         cached token is still valid).
      2. Strips any pre-set ``Authorization`` / ``api-key`` /

View on GitHub (pinned to c896c09c42)

Solutions

  1. Provide either a non-empty static bearer string or a zero-arg callable returning the token.
  2. Check the config path feeding this value (e.g. azure/foundry auth settings) is actually set, not null/blank.
  3. Harden the call site to validate before building the client.

Example fix

# before
http = build_bearer_http_client(cfg.get("token"))  # None when unset
# after
tok = cfg.get("token")
if not (isinstance(tok, str) and tok) and not callable(tok):
    raise ValueError("azure auth token missing in config")
http = build_bearer_http_client(tok)
Defensive patterns

Strategy: type-guard

Validate before calling

value = cfg.get("auth")
assert (isinstance(value, str) and value) or (callable(value) and value.__code__.co_argcount == 0), "auth must be a non-empty string or zero-arg callable"

Type guard

def is_valid_bearer_source(v) -> bool:
    return (isinstance(v, str) and len(v) > 0) or (callable(v) and getattr(v, "__code__", None) is not None and v.__code__.co_argcount == 0)

Try / catch

try:
        materialize_bearer_for_http(value)
except ValueError as e:
    if "no usable api_key / token provider" in str(e):
        raise ConfigError("auth value misconfigured — supply string or () -> str callable") from e

Prevention

When it happens

Trigger: Passing None, an empty string, a number, or a one-arg callable where is_token_provider() is false (agent/azure_identity_adapter.py:475) — e.g. config fed `azure.auth_token: null` into the adapter.

Common situations: Missing config key defaulting to None; passing a static empty token when no Entra credential is configured; SDK version change altering what is_token_provider accepts.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/0c8e254b2fd7bf3e. Report an issue: GitHub.