BerriAI/litellm · error · DatabricksException
OAuth M2M token request failed: {response.text}
Error message
OAuth M2M token request failed: {response.text} What it means
Raised when the Databricks OAuth M2M token endpoint responds but with a non-200 status. The exception carries the endpoint's status code and the raw response body (typically an OAuth error like invalid_client), telling you the request reached the server but authentication failed.
Source
Thrown at litellm/llms/databricks/common_utils.py:252
try:
response: Final = requests.post(
token_url,
data={
"grant_type": "client_credentials",
"scope": "all-apis",
},
auth=(client_id, client_secret),
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=30,
)
except requests.RequestException as e:
raise DatabricksException(
status_code=500,
message=f"OAuth M2M token request failed: {e}",
)
if response.status_code != 200:
raise DatabricksException(
status_code=response.status_code,
message=f"OAuth M2M token request failed: {response.text}",
)
token_data: Final = response.json()
return token_data["access_token"]
def _get_databricks_credentials(
self, api_key: str | None, api_base: str | None, headers: dict | None
) -> tuple[str, dict]:
"""
Get Databricks credentials using the Databricks SDK.
Also registers LiteLLM as a partner for proper telemetry attribution
in Databricks system.access.audit table.
Args:
api_key: Optional API key (PAT)View on GitHub (pinned to 6c2dcb801b)
Solutions
- Read response.text in the message — invalid_client means bad credentials, invalid_scope means permission issues
- Regenerate the service principal secret in Databricks and update DATABRICKS_CLIENT_ID/DATABRICKS_CLIENT_SECRET
- Strip whitespace from secret values loaded from files/vaults
- Confirm the service principal has access to the workspace and serving endpoints
Example fix
# before
client_secret = open("secret.txt").read() # includes trailing \n
# after
client_secret = open("secret.txt").read().strip() Defensive patterns
Strategy: try-catch
Validate before calling
client_id = os.getenv("DATABRICKS_CLIENT_ID", "").strip()
client_secret = os.getenv("DATABRICKS_CLIENT_SECRET", "").strip()
assert client_id and client_secret, "OAuth M2M credentials missing/blank" Try / catch
try:
resp = litellm.completion(model=m, messages=msgs)
except Exception as e:
if "invalid_client" in str(e):
raise ConfigError("Databricks service principal secret is wrong or expired") from e
raise Prevention
- Strip() secrets loaded from files or vaults to remove stray newlines
- Rotate service principal secrets on a schedule and update both env vars atomically
- Alert on any non-200 token response — silent retry of bad credentials only burns lockout budget
When it happens
Trigger: Posting client_credentials grant with a bad client_id/client_secret (400 invalid_client), a service principal secret that expired or was rotated, missing 'all-apis' scope permissions, or a workspace where the service principal cannot authenticate.
Common situations: Rotated Databricks service-principal secrets not updated in env vars; OAuth secrets (not PATs) confused with DATABRICKS_API_KEY; secret typos or trailing whitespace/newlines when copied from a vault.
Related errors
- OAuth M2M token request failed: {e}
- Authentication failed. Check your BitBucket access token and
- Authentication failed. Check your GitLab token and auth_meth
- BedrockException Invalid Authentication - {error_str}
- litellm.BadRequestError: SagemakerException - {error_str}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/6b8dc7c826c1163d.
Report an issue: GitHub.