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

  1. Grant the caller access: gcloud projects add-iam-policy-binding <proj> --member=serviceAccount:<sa> --role=roles/aiplatform.user
  2. For Model Garden / Anthropic models, complete the enablement and terms flow in the Google Cloud console first
  3. Verify VPC Service Controls permits access from your network to the Vertex API
  4. 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

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)