BerriAI/litellm · error · GenAIHubOrchestrationError
{err.args[0]}
Error message
{err.args[0]} What it means
Before the first SAP call, run_env_setup resolves credentials via get_token_creator; any ValueError from that chain (no credentials found in any source, missing auth_url/client_id/base_url, or an ambiguous auth mode - not exactly one of client_secret, cert pair, or cert files) is wrapped as GenAIHubOrchestrationError 400. The message names the exact credential problem.
Source
Thrown at litellm/llms/sap/chat/transformation.py:142
temperature: int | None = None,
top_p: int | None = None,
response_format: dict | None = None,
tools: list | None = None,
tool_choice: str | dict | None = None,
) -> None:
locals_: Final = locals().copy()
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)
self.token_creator = None
self._base_url = None
self._resource_group = None
def run_env_setup(self, service_key: str | None = None) -> None:
try:
self.token_creator, self._base_url, self._resource_group = get_token_creator(service_key)
except ValueError as err:
raise GenAIHubOrchestrationError(status_code=400, message=err.args[0])
@property
def headers(self) -> dict[str, str]:
if self.token_creator is None:
self.run_env_setup()
access_token = self.token_creator() # pyright: ignore[reportOptionalCall] # run_env_setup set it or raised
return {
"Authorization": access_token,
"AI-Resource-Group": self.resource_group,
"Content-Type": "application/json",
"AI-Client-Type": "LiteLLM",
}
@property
def base_url(self) -> str:
if self._base_url is None:
self.run_env_setup()
return self._base_urlView on GitHub (pinned to 77b7c6c40c)
Solutions
- Set the full secret-auth set: AICORE_CLIENT_ID, AICORE_CLIENT_SECRET, AICORE_AUTH_URL, AICORE_BASE_URL (and optionally AICORE_RESOURCE_GROUP).
- Or pass the whole service key once: os.environ['AICORE_SERVICE_KEY'] = json.dumps(service_key_dict) or litellm.sap_service_key = service_key.
- If using certificate auth, provide exactly one of cert_str+key_str or cert_file_path+key_file_path - and remove client_secret.
- Check the wrapped message: 'No credentials found in any source' vs 'credentials are incomplete' tells you whether nothing or only partial config was found.
Example fix
# before - incomplete env os.environ['AICORE_CLIENT_ID'] = '...' # after - complete secret auth os.environ['AICORE_CLIENT_ID'] = '...' os.environ['AICORE_CLIENT_SECRET'] = '...' os.environ['AICORE_AUTH_URL'] = 'https://<tenant>.authentication.sap.hana.ondemand.com/oauth/token' os.environ['AICORE_BASE_URL'] = 'https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v2'
Defensive patterns
Strategy: validation
Validate before calling
import os
REQUIRED_ENV = ['AICORE_CLIENT_ID', 'AICORE_CLIENT_SECRET', 'AICORE_AUTH_URL', 'AICORE_BASE_URL']
missing = [k for k in REQUIRED_ENV if not os.environ.get(k)]
if missing:
raise RuntimeError(f'SAP credentials incomplete, missing: {missing}')
resp = litellm.completion(model='sap/...', messages=msgs) Try / catch
from litellm.llms.sap.chat.handler import GenAIHubOrchestrationError
try:
resp = litellm.completion(model='sap/gpt-4o', messages=msgs)
except GenAIHubOrchestrationError as e:
if e.status_code == 400:
raise RuntimeError(f'SAP credential setup failed: {e.message}') from e
raise Prevention
- Export the full AICORE_* set from one secret manager entry so fields never drift apart.
- Prefer AICORE_SERVICE_KEY with the complete service-key JSON over piecemeal env vars.
- Add a startup credential check that calls fetch_credentials() before serving traffic.
When it happens
Trigger: Calling litellm.completion(model='sap/...') with no AICORE_* environment variables, no AICORE_SERVICE_KEY, no ~/.aicore/config.json, and no service_key passed; or credentials that are incomplete (e.g. client_id and secret set but no AICORE_AUTH_URL/AICORE_BASE_URL); or multiple auth methods supplied at once (secret plus certificate).
Common situations: New environments where only some AICORE_* vars are exported; service keys pasted with missing fields; switching between secret-based and mTLS certificate auth and leaving both partially configured; k8s secrets mounted for a different profile than AICORE_PROFILE selects.
Related errors
- Invalid Argilla API Key given. _credentials_api_key=None.
- Authentication failed. Check your BitBucket access token and
- OAuth M2M token request failed: {response.text}
- GigaChat credentials not provided. Set GIGACHAT_CREDENTIALS
- {json.dumps(event_obj)}
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/afdca9867183538f.
Report an issue: GitHub.