{"record":{"id":"0c8e254b2fd7bf3e","repo":"NousResearch/hermes-agent","slug":"no-usable-api-key-token-provider","errorCode":null,"errorMessage":"no usable api_key / token provider","messagePattern":"no usable api_key / token provider","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/azure_identity_adapter.py","lineNumber":475,"sourceCode":"    accept a ``Callable[[], str]`` for ``auth_token``. Instead,\n    :func:`build_bearer_http_client` returns an ``httpx.Client`` whose\n    request event hook calls this function and rewrites the\n    ``Authorization`` header per request — and that client is passed to\n    the Anthropic SDK via ``http_client=...``. See\n    :func:`agent.anthropic_adapter.build_anthropic_client` for the\n    consumer.\n\n    Raises ``ValueError`` if ``value`` is not a callable token provider\n    or non-empty string.\n    \"\"\"\n    if is_token_provider(value):\n        token = value()\n        if not isinstance(token, str) or not token:\n            raise ValueError(\"token provider returned empty value\")\n        return token\n    if isinstance(value, str) and value:\n        return value\n    raise ValueError(\"no usable api_key / token provider\")\n\n\ndef build_bearer_http_client(token_provider: Callable[[], str], **httpx_kwargs: Any) -> Any:\n    \"\"\"Return an ``httpx.Client`` that mints a fresh Entra bearer JWT\n    per outbound request.\n\n    The Anthropic SDK (≤ 0.86.0 at the time of writing) stores\n    ``api_key`` / ``auth_token`` as static strings and computes the\n    ``Authorization`` header at construction time. To get per-request\n    token refresh (the Microsoft-recommended Foundry pattern for\n    callable bearer providers), we install an httpx ``request`` event\n    hook on a custom client and pass that client to the SDK via\n    ``http_client=...``. The hook:\n\n      1. Calls :func:`materialize_bearer_for_http` to mint a fresh JWT\n         (azure-identity caches internally — this is cheap when the\n         cached token is still valid).\n      2. Strips any pre-set ``Authorization`` / ``api-key`` /","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/azure_identity_adapter.py#L457-L493","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Provide either a non-empty static bearer string or a zero-arg callable returning the token.","Check the config path feeding this value (e.g. azure/foundry auth settings) is actually set, not null/blank.","Harden the call site to validate before building the client."],"exampleFix":"# before\nhttp = build_bearer_http_client(cfg.get(\"token\"))  # None when unset\n# after\ntok = cfg.get(\"token\")\nif not (isinstance(tok, str) and tok) and not callable(tok):\n    raise ValueError(\"azure auth token missing in config\")\nhttp = build_bearer_http_client(tok)","handlingStrategy":"type-guard","validationCode":"value = cfg.get(\"auth\")\nassert (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\"","typeGuard":"def is_valid_bearer_source(v) -> bool:\n    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)","tryCatchPattern":"try:\n        materialize_bearer_for_http(value)\nexcept ValueError as e:\n    if \"no usable api_key / token provider\" in str(e):\n        raise ConfigError(\"auth value misconfigured — supply string or () -> str callable\") from e","preventionTips":["Never pass config values straight into auth builders without shape validation.","Default missing config to an explicit error at load time, not None at call time."],"tags":["azure","validation","authentication","programming-error"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}