BerriAI/litellm · error · ValueError

'username' is required in litellm_params when auth_mode='cp4

Error message

'username' is required in litellm_params when auth_mode='cp4d'

What it means

Raised during watsonx Orchestrate token acquisition when auth_mode='cp4d' but no `username` is present in litellm_params. CP4D authorization posts username + api_key to /icp4d-api/v1/authorize; unlike the default API-key grant, a username is mandatory.

Source

Thrown at litellm/a2a_protocol/providers/watsonx_orchestrate/handler.py:98

        if client is None:
            client = WatsonxOrchestrateHandler._http_client(timeout=30.0)

        if auth_mode == "ibm_cloud":
            response = await client.post(
                _IBM_CLOUD_IAM_URL,
                data={
                    "grant_type": "urn:ibm:params:oauth:grant-type:apikey",
                    "apikey": api_key,
                },
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            )
            response.raise_for_status()
            payload = response.json()
            token = str(payload["access_token"])
            ttl_s = int(payload.get("expires_in", 3600))
        else:
            if not username:
                raise ValueError("'username' is required in litellm_params when auth_mode='cp4d'")
            token_url: Final = f"{cp4d_host.rstrip('/')}/icp4d-api/v1/authorize"
            response = await client.post(
                token_url,
                json={"username": username, "api_key": api_key},
                headers={"Content-Type": "application/json"},
            )
            response.raise_for_status()
            payload = response.json()
            token = str(payload["token"])
            expiration: Final = payload.get("expiration")
            if expiration is None:
                ttl_s = 3600
            else:
                ttl_s = WatsonxOrchestrateHandler._cp4d_token_ttl_seconds(expiration)

        expires_at: Final = now + max(ttl_s - _TOKEN_CACHE_TTL_BUFFER_S, 0)
        _token_cache[cache_key] = (token, expires_at)
        for stale_key, (_, stale_expires_at) in list(_token_cache.items()):

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Add `username` to litellm_params / the watsonx Orchestrate deployment config
  2. If you intended pure API-key auth, remove auth_mode='cp4d' so the default grant-type:apikey flow is used

Example fix

# before
litellm_params={"cp4d_host": host, "instance_id": iid, "wxo_agent_id": aid, "api_key": key, "auth_mode": "cp4d"}
# after
litellm_params={"cp4d_host": host, "instance_id": iid, "wxo_agent_id": aid, "api_key": key, "auth_mode": "cp4d", "username": "user@corp.com"}
Defensive patterns

Strategy: validation

Validate before calling

lp = kwargs.get("litellm_params", {})
if lp.get("auth_mode") == "cp4d" and not lp.get("username"):
    raise ValueError("cp4d auth requires username alongside api_key")

Prevention

When it happens

Trigger: litellm_params has auth_mode='cp4d' (or otherwise not the default apikey grant) and username is None when the handler fetches a token.

Common situations: Switching a deployment from Zen/API-key auth to CP4D user auth and forgetting username; assuming api_key alone is enough because it is for the default mode.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/7c1a1637cd79aa80. Report an issue: GitHub.