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.

Warning

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 runsRecommended auth methodCommon failureFirst thing to check
Local developmentADC via gcloud auth application-default loginADC file missing or expiredgcloud auth application-default print-access-token
Compute EngineAttached service account (metadata server)GOOGLE_APPLICATION_CREDENTIALS set to a missing filecurl -H “Metadata-Flavor: Google” http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
Cloud Run / Cloud FunctionsAttached service account (metadata server)GOOGLE_APPLICATION_CREDENTIALS overriding the metadata serverCheck environment variables in the Cloud Console for stray credential paths
GKEWorkload IdentityMissing annotation or IAM bindingkubectl describe serviceaccount KSA_NAME -n NAMESPACE
GitHub ActionsWorkload Identity FederationIncorrect provider or audienceCheck the workload_identity_provider value in the auth action
TerraformWorkload Identity Federation or service account impersonationKey file expired, or federation misconfiguredterraform plan with TF_LOG=DEBUG
External runner (AWS, Azure, on-prem)Workload Identity FederationToken exchange failure or wrong audienceCheck the federation pool and provider configuration in the GCP Console

Authentication vs authorisation

The HTTP status code tells you which problem you have:

401 = Authentication failure

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.

403 = Authorisation failure

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 with gcloud 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_CREDENTIALS environment 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.254 issues 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.

Quick fix

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-token

ADC 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.

Think of it this way

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

  1. Step 1: GOOGLE_APPLICATION_CREDENTIALS environment variable → reads the file at that path
  2. Step 2: gcloud ADC file at ~/.config/gcloud/application_default_credentials.json
  3. 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_CREDENTIALS is 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_CREDENTIALS
Tip

For 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_CREDENTIALS points 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.com
Key files are like physical house keys

They 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.

Most common self-inflicted failure

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.goog

2. Does the Kubernetes service account have the annotation?

kubectl describe serviceaccount KSA_NAME -n NAMESPACE
# Look for: iam.gke.io/gcp-service-account: GSA_EMAIL

3. 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.workloadIdentityUser

4. Is the pod actually using the correct Kubernetes service account?

kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.spec.serviceAccountName}'
Note

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.

Stop using key files in CI/CD

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.com

Common 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.
Rule of thumb

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

CriteriaADC (user credentials)Attached service accountWorkload Identity FederationService account keys
Works best forLocal developmentGCP-hosted workloadsExternal CI/CD and cross-cloudLegacy or constrained environments
Security levelGood (short-lived, user-scoped)High (no file, auto-rotated)High (short-lived, no secret)Low (long-lived, can be leaked)
Operational overheadLow (one command)Very low (automatic)Medium (pool and provider setup)High (rotation, storage, revocation)
Common mistakesConfusing gcloud login with ADC loginOverriding with GOOGLE_APPLICATION_CREDENTIALSWrong audience or attribute mappingCommitting to git, never rotating
Beginners should use?Yes, for local devYes, on GCPYes, with a guideNo, 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 blocks

Common beginner mistakes

  1. 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.

  2. Using the wrong JSON file type. A user-credential file (“type”: “authorized_user”) and a service account key file (“type”: “service_account”) are different. Pointing GOOGLE_APPLICATION_CREDENTIALS at the wrong type produces confusing errors.

  3. Assuming gcloud auth login is the same as ADC. gcloud auth login sets credentials for gcloud only. Application code uses ADC, which requires gcloud auth application-default login as a separate step.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. Not refreshing ADC after a password change. A Google account password change revokes the ADC refresh token. Run gcloud auth application-default login again.

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.

Last verified: 27 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.