Gemini Vertex AI Auth Failed 401 UNAUTHENTICATED — Fix (2026) | AI Error Hub
GeminiAuthSeverity: HighHTTP 401

Vertex AI Gemini Auth Failed (401 UNAUTHENTICATED)

Vertex AI returned 401 UNAUTHENTICATED — Application Default Credentials (ADC) couldn't authenticate. Common causes: no credentials configured, service account missing roles/aiplatform.user, expired token not refreshed, or workload identity not attached in GKE/Cloud Run.

Error code: UNAUTHENTICATEDHTTP: 401Category: AuthenticationLast tested: Nov 15, 2026
Quick Fix (TL;DR)

Ensure ADC works: local → gcloud auth application-default login; server → GOOGLE_APPLICATION_CREDENTIALS pointing to service account JSON; GKE/Cloud Run → workload identity attached with roles/aiplatform.user. Test with google.auth.default(). Confirm the correct project is selected.

The Error Messages You're Seeing

Response body variants
HTTP/1.1 401 Unauthorized

{
  "error": {
    "code": 401,
    "message": "Request had invalid authentication credentials. 
    Expected OAuth 2 access token, login cookie or other valid 
    authentication credential.",
    "status": "UNAUTHENTICATED"
  }
}

Variants (Python):
google.auth.exceptions.DefaultCredentialsError: Could not 
automatically determine credentials. Please set 
GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials.

google.auth.exceptions.RefreshError: 
('invalid_grant: Token has been expired or revoked.', ...)

google.api_core.exceptions.PermissionDenied: 403 Permission 
'aiplatform.endpoints.predict' denied on resource 
'projects/PROJECT/locations/us-central1/publishers/google/
models/gemini-2.5-pro'.

ADC Credential Sources (Priority Order)

PrioritySourceSet by
1GOOGLE_APPLICATION_CREDENTIALS env varPath to service account JSON
2gcloud user credentialsgcloud auth application-default login
3GCE / GKE / Cloud Run / Cloud Functions metadata serverAttached identity
4Cloud ShellAuto-attached

Required IAM Roles

RolePurpose
roles/aiplatform.userBasic Vertex AI calls (predict, generate)
roles/aiplatform.serviceAgentAdvanced (batch, deployment)
roles/serviceusage.serviceUsageConsumerBill against your project
roles/storage.objectViewerRead GCS URIs (if using multimodal)

What Actually Causes This Error

28%
No credentials configuredFresh env; forgot gcloud auth or env var.
22%
Service account missing aiplatform.user roleAuth passes but authorization fails.
18%
Expired user credentialsADC user tokens expire; refresh needed.
14%
Workload identity not boundGKE pod running as default SA without KSA binding.
10%
Wrong projectCredentials valid but project ID mismatch.
8%
Service account key deleted/rotatedJSON file references revoked key.

Fixes That Work (Tested Nov 2026)

1Diagnose Current Credentials

Python · what's my ADC actually?
import google.auth from google.auth.transport.requests import Request from google.auth import exceptions def diagnose_adc(): try: creds, project = google.auth.default() print(f"Project: {project}") print(f"Credential type: {type(creds).__name__}") if hasattr(creds, "service_account_email"): print(f"Service account: {creds.service_account_email}") elif hasattr(creds, "_service_account_email"): print(f"Service account: {creds._service_account_email}") else: print("User credentials (ADC via gcloud login)") # Force refresh to test creds.refresh(Request()) print(f"Token valid, expires: {creds.expiry}") print(f"Scopes: {creds.scopes}") except exceptions.DefaultCredentialsError as e: print(f"NO ADC: {e}") print("Fix: gcloud auth application-default login") print("Or: export GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json") except exceptions.RefreshError as e: print(f"REFRESH FAILED: {e}") print("Fix: gcloud auth application-default login (re-consent)") diagnose_adc()

2Set Up Each Environment

Bash · env-specific setup
# === LOCAL DEV (Mac/Linux/Windows) === gcloud auth application-default login # Opens browser, grants consent, writes ~/.config/gcloud/application_default_credentials.json gcloud config set project my-project-id gcloud auth application-default set-quota-project my-project-id # === SERVER WITH SERVICE ACCOUNT === # 1. Create SA in Cloud Console → IAM → Service Accounts # 2. Grant roles/aiplatform.user on project # 3. Create key: Actions → Manage keys → Create key → JSON # 4. Set env var export GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/gemini-sa.json" # === CLOUD RUN === # In Cloud Run service settings, set "Service account" to # a SA with roles/aiplatform.user — NO env var or JSON needed. # SDK automatically uses metadata server. gcloud run deploy my-app \ --service-account=gemini-sa@PROJECT.iam.gserviceaccount.com \ --image=gcr.io/PROJECT/my-app # === GKE WORKLOAD IDENTITY === # 1. Enable Workload Identity on cluster # 2. Create KSA and bind to GSA: kubectl create serviceaccount gemini-ksa gcloud iam service-accounts add-iam-policy-binding \ gemini-sa@PROJECT.iam.gserviceaccount.com \ --role=roles/iam.workloadIdentityUser \ --member="serviceAccount:PROJECT.svc.id.goog[NAMESPACE/gemini-ksa]" kubectl annotate serviceaccount gemini-ksa \ iam.gke.io/gcp-service-account=gemini-sa@PROJECT.iam.gserviceaccount.com # 3. Set KSA on pod spec # serviceAccountName: gemini-ksa
Prefer workload identity over JSON keys

Service account JSON files in code repos, containers, or env vars are a top source of credential leaks. Cloud Run and GKE with workload identity give you scoped, rotating credentials without any file to steal. Use JSON keys only when nothing else works.

3Handle Token Refresh in Long-Running Processes

Python · production-ready client
from google import genai from google.api_core import exceptions from google.auth import exceptions as auth_exceptions def get_vertex_client(project, location="us-central1"): """Client with auth pre-check.""" import google.auth from google.auth.transport.requests import Request creds, detected_project = google.auth.default() try: creds.refresh(Request()) except auth_exceptions.RefreshError: raise RuntimeError( "Credentials expired — re-run gcloud auth or rotate SA key" ) return genai.Client( vertexai=True, project=project, location=location ) def generate_with_auth_retry(client, model, contents, max_retries=2): for attempt in range(max_retries + 1): try: return client.models.generate_content( model=model, contents=contents ) except exceptions.Unauthenticated: if attempt == max_retries: raise # Rebuild client — likely token expiry client = get_vertex_client(client._api_client._project)

Preventing This Error Going Forward

  • Run diagnose_adc at app startup. Fail fast on misconfigurations.
  • Prefer workload identity in cloud, gcloud in local dev.
  • Grant aiplatform.user narrowly. Don't over-provision.
  • Rotate JSON keys quarterly. Automate rotation via Secret Manager.
  • Set quota project explicitly. Avoids "which project pays" errors.
  • Handle Unauthenticated retries with credential refresh.
  • Log SA identity + project on startup. Confirms right identity.
SK
Tested by Sana K.
Researcher · AI Error Hub
Last verified: Nov 15, 2026 · SDK: google-genai 0.8+

Frequently Asked Questions

Some support for API keys on Vertex now exists, but the canonical path is service accounts + ADC. For enterprise workloads, stick with ADC; it works better with IAM, audit logging, and VPC-SC. API keys reserved for AI Studio simplicity.

Common: role granted at wrong scope (folder vs project), quota project not set, or credentials cached from previous SA. Refresh: gcloud auth application-default revoke && gcloud auth application-default login.

If the SA calling Vertex is in a different project than the one being billed. Same-project use doesn't require it. When cross-project, grant roles/serviceusage.serviceUsageConsumer on the billing project to the calling SA.

Yes: gcloud auth application-default login --impersonate-service-account=SA_EMAIL. Requires roles/iam.serviceAccountTokenCreator on the target SA. Use for dev/debugging, not production.

Cloud Logging → filter for protoPayload.methodName=~"generateContent" and severity=ERROR. Cross-reference with IAM logs. For token issues, enable audit logs for IAM and Vertex AI.