BerriAI/litellm · error · BadRequestError
Exception BadRequestError
Error message
{custom_llm_provider.capitalize()}Exception BadRequestError - {error_str} What it means
The error string from the Vertex call contains '403' and litellm maps it to BadRequestError (message '<Provider>Exception BadRequestError - <error_str>' with a synthetic 403 response). A 403 from Vertex means the authenticated identity lacks IAM permission on the project or the model is not enabled for the project. Note this string-based branch runs before the status-code branches, so a 403 surfaces as BadRequestError here, not PermissionDeniedError.
Solutions
- Grant the caller access: gcloud projects add-iam-policy-binding <proj> --member=serviceAccount:<sa> --role=roles/aiplatform.user
- For Model Garden / Anthropic models, complete the enablement and terms flow in the Google Cloud console first
- Verify VPC Service Controls permits access from your network to the Vertex API
- Test with user credentials (gcloud auth application-default login) to distinguish IAM problems from network denials
Example fix
# before
litellm.completion(
model='vertex_ai/claude-3-5-sonnet',
messages=messages,
) # 403: model not enabled / SA lacks role
# after
# 1) gcloud projects add-iam-policy-binding my-proj \
# --member=serviceAccount:runner@my-proj.iam.gserviceaccount.com \
# --role=roles/aiplatform.user
# 2) enable Claude in Model Garden (console: Vertex AI -> Model Garden -> Enable)
litellm.completion(
model='vertex_ai/claude-3-5-sonnet@20240620',
messages=messages,
vertex_project='my-proj',
vertex_location='us-east5',
) Defensive patterns
Strategy: try-catch
Validate before calling
from googleapiclient import discovery
def sa_has_aiplatform_role(project_id: str, sa_email: str, creds) -> bool:
crm = discovery.build('cloudresourcemanager', 'v1', credentials=creds)
policy = crm.projects().getIamPolicy(resource=project_id).execute()
for binding in policy.get('bindings', []):
if 'roles/aiplatform.user' in binding['role'] and f'serviceAccount:{sa_email}' in binding['members']:
return True
return False Type guard
import litellm
def is_vertex_403_string(exc: BaseException) -> bool:
return isinstance(exc, litellm.exceptions.BadRequestError) and '403' in str(exc) Try / catch
import litellm
from litellm.exceptions import BadRequestError
try:
resp = litellm.completion(model='vertex_ai/claude-3-5-sonnet', messages=messages)
except BadRequestError as e:
if '403' in str(e):
# IAM/enablement problem surfaced via error text: fix grants, do not retry
raise RuntimeError('vertex 403: grant roles/aiplatform.user and enable the model')
raise Prevention
- Bind roles/aiplatform.user to every service account at project bootstrap
- Complete Model Garden enablement (terms acceptance) for each third-party model before use
- Add an IAM pre-check (getIamPolicy) to deployment pipelines for Vertex-calling services
When it happens
Trigger: Service account missing roles/aiplatform.user; VPC Service Controls perimeter blocking the API; Model Garden models (e.g. Anthropic on Vertex) used without explicit enablement/agreement; cross-project model access without a grant — and the 403 surfaces in the error body text.
Common situations: Newly created service account with no roles bound; org policy or VPC-SC denying egress; teams assuming Claude-on-Vertex works out of the box.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/505138572a0ad5ab.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/litellm_core_utils/exception_mapping_utils.py:1131
message=f"litellm.InternalServerError: {custom_llm_provider}Exception - {error_str}",
model=model,
llm_provider=custom_llm_provider,
response=httpx.Response(
status_code=500,
content=str(original_exception),
request=httpx.Request(method="completion", url="https://github.com/BerriAI/litellm"),
),
litellm_debug_info=extra_information,
)
elif "API key not valid." in error_str:
raise AuthenticationError(
message=f"{custom_llm_provider.capitalize()}Exception - {error_str}",
model=model,
llm_provider=custom_llm_provider,
litellm_debug_info=extra_information,
)
elif "403" in error_str:
raise BadRequestError(
message=f"{custom_llm_provider.capitalize()}Exception BadRequestError - {error_str}",
model=model,
llm_provider=custom_llm_provider,
response=httpx.Response(
status_code=403,
request=httpx.Request(
method="POST",
url=" https://cloud.google.com/vertex-ai/",
),
),
litellm_debug_info=extra_information,
)
elif (
"The response was blocked." in error_str
or "Output blocked by content filtering policy" in error_str # anthropic on vertex ai
):
raise ContentPolicyViolationError(
message=f"{custom_llm_provider.capitalize()}Exception ContentPolicyViolationError - {error_str}",View on GitHub (pinned to 77b7c6c40c)