{"record":{"id":"029e15484d547697","repo":"NousResearch/hermes-agent","slug":"token-provider-returned-empty-value","errorCode":null,"errorMessage":"token provider returned empty value","messagePattern":"token provider returned empty value","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/azure_identity_adapter.py","lineNumber":471,"sourceCode":"    header outside the OpenAI SDK (e.g. ``hermes_cli/azure_detect.py``).\n    Calls the callable exactly once and returns the resulting token.\n\n    **Anthropic SDK integration:** the Anthropic Python SDK does not\n    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","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/azure_identity_adapter.py#L453-L489","documentation":"materialize_bearer_for_http() accepted a zero-arg callable token provider and invoked it, but the callable returned None or an empty/non-string value. In practice the callable wraps DefaultAzureCredential/azure-identity chain minting an Entra bearer JWT; an empty token means the credential chain ran but produced nothing usable.","triggerScenarios":"build_bearer_http_client(token_provider) where token_provider() returns '' or None — e.g. azure credential chain silently exhausted, or a custom provider function with a bug returning nothing on error (agent/azure_identity_adapter.py:471).","commonSituations":"`az login` expired; managed identity unavailable (wrong VM/containers environment); tenant/subscription env vars (AZURE_TENANT_ID etc.) misconfigured; a stub token provider in tests returning None.","solutions":["Re-authenticate: `az login` (and `az account set --subscription ...`).","Verify the credential chain works standalone: `az account get-access-token --resource https://ai.azure.com` (or the Foundry resource) in the same shell.","Set required AZURE_* env vars (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET for service principals).","If it is a custom token provider, make it raise on failure instead of returning None/empty."],"exampleFix":"# custom token provider — before\nasync def token_provider():\n    return await cache.get(\"token\")  # may be None\n# after\ndef token_provider() -> str:\n    token = fetch_entra_token()\n    if not token:\n        raise RuntimeError(\"failed to acquire Entra token\")\n    return token","handlingStrategy":"validation","validationCode":"def token_ok(provider) -> bool:\n    try:\n        t = provider()\n    except Exception:\n        return False\n    return isinstance(t, str) and bool(t)\nassert token_ok(my_token_provider), \"Entra token provider yields no usable token\"","typeGuard":"def is_usable_token_provider(fn) -> bool:\n    if not callable(fn):\n        return False\n    try:\n        t = fn()\n    except Exception:\n        return False\n    return isinstance(t, str) and len(t) > 0","tryCatchPattern":"try:\n    client = build_bearer_http_client(token_provider)\n    ...  # first request\nexcept ValueError as e:\n    if \"token provider returned empty value\" in str(e):\n        relogin_or_refresh_credentials()  # az login etc.","preventionTips":["Keep `az login` sessions fresh; script token refresh before long runs.","Validate the token provider once at startup, not per request.","Make custom providers raise on failure rather than returning None."],"tags":["azure","entra-id","authentication","token"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}