Fix GCP Authentication Errors: ADC, Service Accounts, Cloud Run, GKE, and CI/CD
If your GCP API calls are failing with “could not load the default credentials,” a 401 error,
or “credentials not found,” this page walks you through diagnosing and fixing the problem.
The most common scenarios are: gcloud works but your application code fails, a Cloud Run or
Compute Engine service cannot load credentials, GOOGLE_APPLICATION_CREDENTIALS
is blocking the metadata server, GKE Workload Identity is only half-configured, or an external
CI/CD pipeline like GitHub Actions or Terraform is using the wrong authentication method.
Every fix on this page follows the same principle: identify which credentials your code is actually using, confirm they are valid, and verify they match the environment. Start with the quick diagnosis table below, then jump to the section that matches your situation.
Simple explanation
Think of authentication like showing your passport at the airport. If the passport is expired or you left it at home, you cannot board. That is an authentication error: GCP cannot verify who is making the request. Credentials are missing, expired, or misconfigured, and no API call can succeed until this is fixed.
Authorisation is what happens next: the airline checks whether you have a valid ticket for that specific flight. A permission error (403) means GCP knows who you are but IAM says you are not allowed to do what you asked. If you are seeing 403 errors, authentication is already working and you need to fix permissions instead.
“Credentials” in GCP means proof of identity. That proof can come from a user login, a service account key file, a short-lived token from the metadata server, or a federated identity token from an external provider. Your code does not always know which one it is using, and that is often the root cause of the error.
Do not skip ahead to debugging IAM roles and bindings if your error is a 401 or “could not load credentials.” Fix authentication first. A 403 that appears after you fix credentials is a completely separate issue with a different solution.
Quick diagnosis: start here
Find the environment where your code runs. The recommended authentication method, the most common failure, and the first thing to check are different for each one.
| Where the code runs | Recommended auth method | Common failure | First thing to check |
|---|---|---|---|
| Local development | ADC via gcloud auth application-default login | ADC file missing or expired | gcloud auth application-default print-access-token |
| Compute Engine | Attached service account (metadata server) | GOOGLE_APPLICATION_CREDENTIALS set to a missing file | curl -H “Metadata-Flavor: Google” http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email |
| Cloud Run / Cloud Functions | Attached service account (metadata server) | GOOGLE_APPLICATION_CREDENTIALS overriding the metadata server | Check environment variables in the Cloud Console for stray credential paths |
| GKE | Workload Identity | Missing annotation or IAM binding | kubectl describe serviceaccount KSA_NAME -n NAMESPACE |
| GitHub Actions | Workload Identity Federation | Incorrect provider or audience | Check the workload_identity_provider value in the auth action |
| Terraform | Workload Identity Federation or service account impersonation | Key file expired, or federation misconfigured | terraform plan with TF_LOG=DEBUG |
| External runner (AWS, Azure, on-prem) | Workload Identity Federation | Token exchange failure or wrong audience | Check the federation pool and provider configuration in the GCP Console |
Authentication vs authorisation
The HTTP status code tells you which problem you have:
Messages like “could not load credentials,” “credentials not found,” or “invalid token.” GCP cannot verify who is calling. Fix credentials. You are on the right page.
Messages like “PERMISSION_DENIED” or “insufficient permissions.” GCP knows who you are but IAM says no. Authentication is working. Fix IAM bindings instead.
Always fix authentication first. A 403 that appears after fixing a 401 is expected and has a completely different fix.
How GCP authentication works
GCP supports several credential types. Understanding which one your code is using is the fastest way to diagnose an authentication error.
User credentials. Your Google account, obtained through
gcloud auth login. Used by gcloud and the Console. Not used by application code unless you explicitly set up ADC withgcloud auth application-default login.Application Default Credentials (ADC). The automatic discovery mechanism that GCP client libraries use. ADC checks three places in order: (1) the
GOOGLE_APPLICATION_CREDENTIALSenvironment variable, (2) the gcloud ADC file at~/.config/gcloud/application_default_credentials.json, (3) the metadata server. It stops at the first one that exists.Attached service account / metadata server. On Compute Engine, Cloud Run, Cloud Functions, and GKE (without Workload Identity), the metadata server at
169.254.169.254issues short-lived tokens for the service account attached to the resource. No key file needed.Workload Identity (GKE). Maps a Kubernetes service account to a GCP service account. Pods get tokens from the metadata server as if they were the GCP service account.
Workload Identity Federation. Lets external identities (GitHub Actions, AWS, Azure, on-prem OIDC) exchange their native token for a short-lived GCP access token. No key file needed.
Service account impersonation. Lets an authenticated identity generate short-lived credentials as a different service account. Useful for local development and admin tasks.
Service account keys. Long-lived JSON key files. These are the legacy pattern. They work everywhere but create significant security risk. Avoid them unless no alternative exists.
Most common GCP authentication errors and what they usually mean
”Could not load the default credentials”
ADC found nothing at any of its three lookup steps. On a local machine, run
gcloud auth application-default login. On a GCP service, check whether
GOOGLE_APPLICATION_CREDENTIALS is set to a path that does not exist in the runtime
environment.
”Credentials not found” or “unable to detect credentials”
Same root cause as above. The client library tried all ADC steps and none returned usable credentials. Check the environment variable, the ADC file, and (on GCP) the metadata server.
”Invalid token” or “token expired”
The credential exists but the token it produced is not valid. On a local machine, re-run
gcloud auth application-default login. If using a service account key, confirm
the key has not been deleted from the GCP Console. Client libraries refresh tokens
automatically, so if you see this in application code, the underlying credential is likely
broken.
”Error creating credential from JSON” or “wrong credential file type”
GOOGLE_APPLICATION_CREDENTIALS points to the wrong kind of JSON file. The most
common mistake is pointing it to a gcloud user-credential file instead of a service account key
file. The user-credential file lives at
~/.config/gcloud/application_default_credentials.json and has a
“type”: “authorized_user” field. A service account key file has
“type”: “service_account”. Make sure the variable points to the correct file.
gcloud works but application code fails
gcloud and your application use different credentials. gcloud uses the login from
gcloud auth login. Your app uses ADC. If you have not run
gcloud auth application-default login, ADC has no credentials even though gcloud
does. Run it, or check whether GOOGLE_APPLICATION_CREDENTIALS is pointing
somewhere unexpected.
This is the single most common “works on my terminal, fails in my code” issue. Run
gcloud auth application-default login and try again. That one command fixes
it in most cases.
# Verify gcloud credentials (used by gcloud CLI)
gcloud auth list
# Verify ADC credentials (used by your application code)
gcloud auth application-default print-access-tokenADC problems
Application Default Credentials is the most common source of authentication errors because developers do not always know which step of the lookup chain their code is hitting.
ADC works like a search path. Just like your terminal checks PATH directories
in order to find an executable, ADC checks three locations in order to find credentials. If
it finds something at the first location, even a broken file, it stops looking. It never
checks the next location.
The ADC lookup order
- Step 1:
GOOGLE_APPLICATION_CREDENTIALSenvironment variable → reads the file at that path - Step 2: gcloud ADC file at
~/.config/gcloud/application_default_credentials.json - Step 3: metadata server (only available on GCP infrastructure)
The lookup stops at the first step that matches. If step 1 is set but the file is missing, ADC fails immediately. It does not fall through to step 2 or 3.
If ADC is not working
- Check whether
GOOGLE_APPLICATION_CREDENTIALSis set:echo $GOOGLE_APPLICATION_CREDENTIALS - If it is set, confirm the file exists at that path and contains valid JSON
- If it is not set, check whether the gcloud ADC file exists:
ls ~/.config/gcloud/application_default_credentials.json - If neither exists and you are on GCP, verify the metadata server is reachable
# Set up ADC for local development
gcloud auth application-default login
# Confirm ADC is working
gcloud auth application-default print-access-token
# Check the environment variable
echo $GOOGLE_APPLICATION_CREDENTIALS
# Unset it if it is causing problems
unset GOOGLE_APPLICATION_CREDENTIALSFor local development, prefer gcloud auth application-default login over
downloading key files. It uses your Google identity, refreshes automatically, and avoids
the risk of accidentally committing a key file to version control. For more control, consider
service account impersonation
to test with the exact permissions your production service account has.
Service account key file problems
If your application authenticates with a service account key file, these are the most common failure modes:
- File not found:
GOOGLE_APPLICATION_CREDENTIALSpoints to a path that does not exist in the current environment (common when deploying to a container that does not include the key file) - Invalid JSON: the file is corrupted, truncated, or is not a service account key file at all
- Key deleted server-side: the key was deleted in the GCP Console, but the local file still exists. The file looks valid but the key ID no longer works.
- Service account disabled or deleted: the service account that owns the key no longer exists or has been disabled
- Wrong JSON file type: the file is a user-credential JSON (
“type”: “authorized_user”) instead of a service account key (“type”: “service_account”)
# List active keys for a service account
gcloud iam service-accounts keys list \
--iam-account=my-sa@my-project.iam.gserviceaccount.com
# Check if the service account is disabled
gcloud iam service-accounts describe \
my-sa@my-project.iam.gserviceaccount.com \
--format="value(disabled)"
# Re-enable a disabled service account
gcloud iam service-accounts enable \
my-sa@my-project.iam.gserviceaccount.comThey work, but if someone copies the key, they have permanent access until you change the lock. Service account keys are long-lived credentials that can be leaked, stolen, or committed to version control. They are a significant security risk. Before troubleshooting a key file, consider whether you can switch to Workload Identity Federation, service account impersonation, or attached service accounts instead.
Metadata server issues on Google Cloud
On Compute Engine, Cloud Run, Cloud Functions, and GKE, the metadata server at
169.254.169.254 provides credentials automatically. GCP client libraries use it
as step 3 of the ADC lookup. When it works, you do not need any key files or environment
variables.
Why setting GOOGLE_APPLICATION_CREDENTIALS on managed runtimes causes failures
Think of the metadata server as a concierge desk built into every GCP building. If you are inside the building, just walk up and ask for credentials. But if you tape a sign on the front door saying “go to a different address,” you will never reach the concierge.
If GOOGLE_APPLICATION_CREDENTIALS is set to a file path that does not exist
inside the container or VM, ADC stops at step 1 and fails. It never reaches the metadata
server at step 3. The fix: remove the environment variable entirely and let ADC fall
through to the metadata server.
If the metadata server is not responding
- Confirm you are running on GCP infrastructure (the metadata server is not available on local machines or external clouds)
- Check that the service account attached to the resource is not disabled or deleted
- On Compute Engine, verify the instance was created with a service account (the default service account may have been removed)
# Test metadata server access from a VM or container
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email"
# Get a token from the metadata server directly
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"GKE Workload Identity authentication failures
Workload Identity lets GKE pods authenticate as a GCP service account without key files. It requires configuration on both sides: the Kubernetes service account and the GCP service account. Missing either side causes authentication to fail silently.
Step-by-step diagnosis
1. Is Workload Identity enabled on the cluster?
gcloud container clusters describe CLUSTER_NAME \
--zone=ZONE \
--format="value(workloadIdentityConfig.workloadPool)"
# Must return PROJECT_ID.svc.id.goog2. Does the Kubernetes service account have the annotation?
kubectl describe serviceaccount KSA_NAME -n NAMESPACE
# Look for: iam.gke.io/gcp-service-account: GSA_EMAIL3. Does the GCP service account have the IAM binding?
gcloud iam service-accounts get-iam-policy GSA_EMAIL --format="yaml"
# Must include:
# members:
# - serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]
# role: roles/iam.workloadIdentityUser4. Is the pod actually using the correct Kubernetes service account?
kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.spec.serviceAccountName}'Workload Identity requires both sides of the binding. The Kubernetes service account needs
the iam.gke.io/gcp-service-account annotation, and the GCP
service account needs a binding granting roles/iam.workloadIdentityUser to
serviceAccount:PROJECT.svc.id.goog[NAMESPACE/KSA_NAME]. If either side is
missing, the pod falls back to the node’s service account or gets no credentials at all.
External workloads and CI/CD authentication
Workloads running outside of GCP (GitHub Actions, Terraform Cloud, Jenkins, GitLab CI, or applications on AWS and Azure) need to authenticate to GCP without a metadata server. The recommended approach is Workload Identity Federation, which exchanges the runner’s native identity token for a short-lived GCP access token.
Storing a service account key as a CI/CD secret creates a long-lived credential that must be rotated manually. If the secret leaks, an attacker has persistent access. Workload Identity Federation eliminates the secret entirely by exchanging short-lived tokens instead.
GitHub Actions
Use the google-github-actions/auth action with
workload_identity_provider instead of a service account key. This eliminates
the need to store a key as a GitHub secret.
# .github/workflows/deploy.yml (simplified)
- id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL/providers/PROVIDER
service_account: deploy-sa@PROJECT_ID.iam.gserviceaccount.comCommon failures: wrong workload_identity_provider value, missing attribute
mapping in the provider configuration, or the service account not granting
roles/iam.workloadIdentityUser to the federated principal.
Terraform
Terraform can authenticate through Workload Identity Federation, a service account key file, or ADC. For CI/CD pipelines, federation is preferred. For local runs, use ADC with service account impersonation so you test with the same permissions as your deployment service account.
# Terraform provider with impersonation (local development)
provider "google" {
project = "my-project"
region = "us-central1"
impersonate_service_account = "terraform-sa@my-project.iam.gserviceaccount.com"
}Workloads on AWS or Azure
Use Workload Identity Federation with the cloud provider’s identity system as the OIDC or SAML provider. For example, an AWS Lambda function can exchange its IAM role credentials for a GCP token through a configured federation pool. No GCP key file needs to exist on the external cloud.
When to use service account impersonation instead
Service account impersonation is the better pattern for local development, admin tasks, and testing. It lets your user account generate short-lived credentials as a service account without downloading a key file. It also creates a clear audit trail in Cloud Audit Logs showing who impersonated which service account and when.
When to use each authentication method
- Local development: ADC with user credentials (
gcloud auth application-default login), or service account impersonation for production-like permissions - Google Cloud runtime (Compute Engine, Cloud Run, Cloud Functions): attached service account via the metadata server. Do not set
GOOGLE_APPLICATION_CREDENTIALS. - GKE: Workload Identity. Map Kubernetes service accounts to GCP service accounts.
- External CI/CD (GitHub Actions, GitLab, Jenkins): Workload Identity Federation
- External cloud (AWS, Azure): Workload Identity Federation with the native identity provider
- Admin and testing: service account impersonation
- Service account keys: avoid unless there is a strong technical reason and no alternative exists. Understand the risks first.
If you are inside a GCP building (VM, container, function), the building gives you credentials automatically. If you are outside (CI/CD, another cloud, your laptop), use federation or impersonation. Only carry a physical key (key file) if there is truly no other way in.
ADC vs attached service account vs Workload Identity Federation vs service account keys
| Criteria | ADC (user credentials) | Attached service account | Workload Identity Federation | Service account keys |
|---|---|---|---|---|
| Works best for | Local development | GCP-hosted workloads | External CI/CD and cross-cloud | Legacy or constrained environments |
| Security level | Good (short-lived, user-scoped) | High (no file, auto-rotated) | High (short-lived, no secret) | Low (long-lived, can be leaked) |
| Operational overhead | Low (one command) | Very low (automatic) | Medium (pool and provider setup) | High (rotation, storage, revocation) |
| Common mistakes | Confusing gcloud login with ADC login | Overriding with GOOGLE_APPLICATION_CREDENTIALS | Wrong audience or attribute mapping | Committing to git, never rotating |
| Beginners should use? | Yes, for local dev | Yes, on GCP | Yes, with a guide | No, unless required |
Verifying credentials in code
# Python: check which credentials ADC resolves to
import google.auth
import google.auth.transport.requests
credentials, project = google.auth.default()
print(f"Credential type: {type(credentials).__name__}")
print(f"Project: {project}")
# Request a fresh token to verify the credentials work
request = google.auth.transport.requests.Request()
credentials.refresh(request)
print(f"Token obtained: {credentials.token[:20]}...")# Verify credentials work with a curl test (any language)
TOKEN=$(gcloud auth print-access-token)
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
"https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID"
# 200 = auth works; 401 = credentials invalid; 403 = auth works but IAM blocksCommon beginner mistakes
Setting GOOGLE_APPLICATION_CREDENTIALS everywhere. On managed GCP services (Cloud Run, Compute Engine, Cloud Functions), the metadata server provides credentials automatically. Setting the variable to a path that does not exist in the runtime breaks ADC at step 1 instead of letting it reach the metadata server.
Using the wrong JSON file type. A user-credential file (
“type”: “authorized_user”) and a service account key file (“type”: “service_account”) are different. PointingGOOGLE_APPLICATION_CREDENTIALSat the wrong type produces confusing errors.Assuming gcloud auth login is the same as ADC.
gcloud auth loginsets credentials for gcloud only. Application code uses ADC, which requiresgcloud auth application-default loginas a separate step.Shipping key files in container images. Baking a key file into a Docker image means every copy of that image contains the secret. Anyone who pulls the image has the key. Use the metadata server or Workload Identity Federation instead.
Debugging IAM before fixing credentials. If the error is 401, the problem is credentials, not IAM. Fix authentication first. A 403 after fixing a 401 is a separate problem with a different solution.
Using key files in GitHub Actions when federation is available. Storing a service account key as a GitHub secret creates a long-lived credential that must be rotated manually. Workload Identity Federation eliminates the secret entirely.
Committing service account key files to version control. A key in a git repository is a critical security incident. Add key files to
.gitignore, use Secret Manager for secrets that must be stored, and rotate any key that was committed.Not refreshing ADC after a password change. A Google account password change revokes the ADC refresh token. Run
gcloud auth application-default loginagain.
Summary
- 401 = authentication (credentials); 403 = authorisation (IAM). Fix the right one.
- ADC lookup order: GOOGLE_APPLICATION_CREDENTIALS, then gcloud ADC file, then metadata server. It stops at the first match.
- Setting GOOGLE_APPLICATION_CREDENTIALS to a missing file blocks the metadata server fallback
- On GCP runtimes, remove GOOGLE_APPLICATION_CREDENTIALS and let ADC use the metadata server
- GKE Workload Identity needs both the Kubernetes annotation and the GCP IAM binding
- External workloads should use Workload Identity Federation, not key files
- Use
gcloud auth application-default loginfor local development, not key files - Client libraries handle token refresh automatically. Do not cache tokens manually.
Frequently asked questions
Why do my gcloud commands work but my app still gets authentication errors?
gcloud and your application code use different credential stores. gcloud uses the credentials from gcloud auth login, while your application code uses Application Default Credentials (ADC), which follow a separate lookup chain: first the GOOGLE_APPLICATION_CREDENTIALS environment variable, then the gcloud ADC file created by gcloud auth application-default login, then the metadata server. If you have not run gcloud auth application-default login, or if GOOGLE_APPLICATION_CREDENTIALS points to a missing file, your app fails even though gcloud works. Run gcloud auth application-default print-access-token to check whether ADC is configured.
Should I use a service account key file or Workload Identity Federation?
Use Workload Identity Federation whenever possible. Key files are long-lived secrets that can be leaked, committed to version control, or stolen. Workload Identity Federation issues short-lived tokens through identity exchange with no secret to manage. Use it for GitHub Actions, Terraform Cloud, and workloads on AWS or Azure. Only use key files when federation is technically impossible and no other option exists.
Why did setting GOOGLE_APPLICATION_CREDENTIALS break my Cloud Run service?
Cloud Run provides credentials automatically through the metadata server. When you set GOOGLE_APPLICATION_CREDENTIALS, the ADC lookup stops at step 1 and tries to read the file at that path. If the file does not exist inside the container, authentication fails immediately instead of falling through to the metadata server. Remove the environment variable and let ADC reach the metadata server naturally.
What is the difference between ADC and service account impersonation?
ADC is the automatic credential-discovery mechanism that GCP client libraries use to find credentials. Service account impersonation lets an authenticated identity (your user account or another service account) generate short-lived tokens as a different service account, without needing a key file. Impersonation is useful for local development and testing when you want to simulate production credentials without downloading keys.
How do I authenticate Terraform or GitHub Actions to GCP without a key file?
Use Workload Identity Federation. For GitHub Actions, configure a Workload Identity Pool with an OIDC provider that trusts your GitHub repository, then use the google-github-actions/auth action with workload_identity_provider. For Terraform, configure a federation pool and set the credential source in your provider block. Both approaches issue short-lived tokens and eliminate the need to store or rotate key files.