{"record":{"id":"7c1a1637cd79aa80","repo":"BerriAI/litellm","slug":"username-is-required-in-litellm-params-when-auth","errorCode":null,"errorMessage":"'username' is required in litellm_params when auth_mode='cp4d'","messagePattern":"'username' is required in litellm_params when auth_mode='cp4d'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/a2a_protocol/providers/watsonx_orchestrate/handler.py","lineNumber":98,"sourceCode":"        if client is None:\n            client = WatsonxOrchestrateHandler._http_client(timeout=30.0)\n\n        if auth_mode == \"ibm_cloud\":\n            response = await client.post(\n                _IBM_CLOUD_IAM_URL,\n                data={\n                    \"grant_type\": \"urn:ibm:params:oauth:grant-type:apikey\",\n                    \"apikey\": api_key,\n                },\n                headers={\"Content-Type\": \"application/x-www-form-urlencoded\"},\n            )\n            response.raise_for_status()\n            payload = response.json()\n            token = str(payload[\"access_token\"])\n            ttl_s = int(payload.get(\"expires_in\", 3600))\n        else:\n            if not username:\n                raise ValueError(\"'username' is required in litellm_params when auth_mode='cp4d'\")\n            token_url: Final = f\"{cp4d_host.rstrip('/')}/icp4d-api/v1/authorize\"\n            response = await client.post(\n                token_url,\n                json={\"username\": username, \"api_key\": api_key},\n                headers={\"Content-Type\": \"application/json\"},\n            )\n            response.raise_for_status()\n            payload = response.json()\n            token = str(payload[\"token\"])\n            expiration: Final = payload.get(\"expiration\")\n            if expiration is None:\n                ttl_s = 3600\n            else:\n                ttl_s = WatsonxOrchestrateHandler._cp4d_token_ttl_seconds(expiration)\n\n        expires_at: Final = now + max(ttl_s - _TOKEN_CACHE_TTL_BUFFER_S, 0)\n        _token_cache[cache_key] = (token, expires_at)\n        for stale_key, (_, stale_expires_at) in list(_token_cache.items()):","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/a2a_protocol/providers/watsonx_orchestrate/handler.py#L80-L116","documentation":"Watsonx Orchestrate auth supports two modes. auth_mode='iam' exchanges the apikey with IAM for a bearer token; the default auth_mode='cp4d' authenticates against Cloud Pak for Data (CP4D), which requires a username in addition to the api_key. When cp4d mode is selected and username is absent from litellm_params, the handler raises this ValueError before attempting the /icp4d-api/v1/authorize call.","triggerScenarios":"A watsonx_orchestrate deployment with auth_mode: cp4d (or auth_mode unset, since cp4d is the default) and no username in litellm_params; upgrading configs where username was previously implicit; switching an IAM-configured agent to a CP4D-hosted instance without adding the username.","commonSituations":"Configuring WXO agents for an on-prem Cloud Pak for Data deployment and only supplying api_key; assuming the IAM-style apikey-only setup works for CP4D; typos like user: instead of username:; forgetting that omitting auth_mode silently selects cp4d.","solutions":["Add username (e.g. the CP4D admin or service user) to the deployment's litellm_params","If the instance authenticates via IAM instead, set auth_mode: iam and keep only api_key","Confirm cp4d_host points at the CP4D cluster reachable from the proxy","Reload/restart the proxy so the corrected litellm_params take effect"],"exampleFix":"# before (config.yaml)\nlitellm_params:\n  model: watsonx_orchestrate/my-agent\n  cp4d_host: https://cp4d.example.com\n  api_key: os.environ/WXO_API_KEY\n\n# after\nlitellm_params:\n  model: watsonx_orchestrate/my-agent\n  cp4d_host: https://cp4d.example.com\n  api_key: os.environ/WXO_API_KEY\n  username: admin\n  auth_mode: cp4d\n","handlingStrategy":"validation","validationCode":"def validate_wxo_auth(litellm_params: dict) -> None:\n    auth_mode = litellm_params.get(\"auth_mode\", \"cp4d\")\n    if auth_mode == \"cp4d\" and not litellm_params.get(\"username\"):\n        raise ValueError(\"username required for auth_mode=cp4d; add it or switch auth_mode to iam\")\n","typeGuard":null,"tryCatchPattern":"try:\n    result = await WatsonxOrchestrateHandler.handle_request(params)\nexcept ValueError as e:\n    if \"'username' is required\" in str(e):\n        raise HTTPException(500, \"WXO deployment missing username for cp4d auth\") from e\n    raise\n","preventionTips":["Document per auth_mode which keys are mandatory (cp4d: username+api_key; iam: api_key)","Add a config lint rule for watsonx_orchestrate deployments covering auth fields","Prefer env-var indirection for username and api_key and check them at startup"],"tags":["watsonx-orchestrate","cp4d","authentication","username","configuration"],"backgroundTag":"missing-auth-credentials","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}