$ gcloud troubleshoot
# GCP Troubleshooting
The errors people actually hit on Google Cloud: symptom → cause → fix → official docs. Search or filter by area, then click a card to expand.
Catch these before they bite: gcphelpit is our free open-source CLI that scans a snapshot of your Google Cloud project for security, IAM, cost, and reliability issues, each with a plain-English fix. See how it compares to other GCP scanners.
all
billing
iam / permissions
quotas
apis
auth / gcloud
compute
networking
storage
serverless
gke
bigquery
databases
logging / monitoring
terraform / iac
// no matching fixes — try a different term or category.
💸 Billing & cost surprises
▸"This API method requires billing to be enabled" / project has no billing account
- Cause
- The project isn't linked to an active Cloud Billing account, so paid services (and even some API calls) are blocked.
- Fix
- Console → Billing → Link a billing account, or run:
Verify with
gcloud billing projects link PROJECT_ID --billing-account=XXXXXX-XXXXXX-XXXXXXgcloud billing projects describe PROJECT_ID. - Docs
- cloud.google.com/billing/docs/how-to/modify-project
▸Unexpected charges / bill much higher than expected
- Cause
- Always-on resources — idle VMs, static IPs on stopped VMs, load balancers, provisioned disks, NAT gateways, logging — keep billing even when "not used."
- Fix
- Billing → Reports to find the top service/SKU; delete or stop idle resources; release unused static IPs (charged when unattached); act on cost recommendations in the Recommender.
- Docs
- cloud.google.com/billing/docs/how-to/reports
▸A budget was set but spending wasn't capped
- Cause
- Budgets are alerting only — they never stop usage or billing automatically.
- Fix
- Create budgets with threshold alerts (Billing → Budgets & alerts). For a true hard stop, wire the budget's Pub/Sub notification to a Cloud Function that disables billing.
- Docs
- budgets · disable billing with notifications
▸Free-tier / $300 trial confusion (charged after trial or over limits)
- Cause
- The $300 credit expires after 90 days; "Always Free" tiers have monthly caps that don't roll over and exclude many regions/sizes.
- Fix
- Check per-product free limits before deploying; keep VMs to the eligible
e2-microregion/size; monitor the credit balance in Billing. - Docs
- cloud.google.com/free/docs/free-cloud-features
▸Budget alert emails not arriving
- Cause
- Alerts default to Billing Account Admins/Users; custom recipients or Pub/Sub/Monitoring channels weren't configured.
- Fix
- Edit the budget → Manage notifications → add email channels via Cloud Monitoring, or add members with a Billing role.
- Docs
- cloud.google.com/billing/docs/how-to/budgets
🔐 IAM & permissions
▸"PERMISSION_DENIED: <email> does not have <service>.<resource>.<verb> access"
- Cause
- The principal lacks a role containing that specific permission (or a deny policy blocks it).
- Fix
- Read the exact missing permission from the error, then grant a role that includes it:
Use the suggested-roles list Google prints in the error.
gcloud projects add-iam-policy-binding PROJECT_ID \ --member=user:EMAIL --role=roles/ROLE - Docs
- iam/docs/permission-error-messages
▸"The caller does not have permission"
- Cause
- Same as above but often on the service agent or a parent resource (folder/org), or you're authenticated as the wrong identity.
- Fix
- Confirm the active identity with
gcloud auth list, then use the Policy Troubleshooter to see which policy grants/denies access. - Docs
- iam/docs/troubleshooting-access
▸Confused about roles vs. permissions
- Cause
- You can't grant a single permission directly — permissions are bundled into roles (basic, predefined, custom).
- Fix
- Prefer predefined roles scoped to the service; create a custom role only if none fits. Grant at the lowest resource level needed.
- Docs
- iam/docs/understanding-roles
▸Service account "does not have permission" when running code
- Cause
- The runtime service account (default Compute/Cloud Run SA) lacks roles for the resources the code touches.
- Fix
- Identify the SA the workload runs as and grant it the needed role(s). Prefer attaching a dedicated least-privilege SA over the default one.
- Docs
- best-practices-service-accounts
▸Action blocked by an Organization Policy (can't create SA keys / allow public access / share externally)
- Cause
- An org policy constraint (e.g.
iam.disableServiceAccountKeyCreation,iam.allowedPolicyMemberDomains,storage.publicAccessPrevention) overrides IAM. - Fix
- An Org Policy Admin must adjust/override the constraint at project/folder level, or use the allowed alternative (e.g. Workload Identity instead of SA keys).
- Docs
- organization-policy/using-constraints
▸IAM change "didn't work" immediately
- Cause
- IAM policy changes are usually fast but can take up to ~7 minutes to propagate.
- Fix
- Wait a few minutes and retry before deeper debugging.
- Docs
- iam/docs/access-change-propagation
📊 Quotas & limits
▸"Quota exceeded" / RESOURCE_EXHAUSTED (CPUs, IN_USE_ADDRESSES, …)
- Cause
- You hit a per-project, per-region quota — many default low, especially for new/trial accounts.
- Fix
- IAM & Admin → Quotas & System Limits → filter by the service/metric → select → Edit quotas to request an increase. Deleting/stopping resources frees region-scoped quota immediately.
- Docs
- quotas/view-manage · troubleshoot
▸Quota increase denied / trial account can't raise quota
- Cause
- Free-trial billing accounts have restricted quotas (e.g. 8 vCPUs) and limited increase eligibility.
- Fix
- Upgrade to a paid/self-serve billing account, then re-request the increase.
- Docs
- cloud.google.com/docs/quotas/troubleshoot
▸Surprise quota hit at scale
- Cause
- No proactive monitoring of usage vs. limit.
- Fix
- Set up Cloud Quotas alerts to notify at a % of the limit.
- Docs
- quotas/set-up-quota-alerts
🔌 APIs not enabled
▸"API [name] has not been used in project N before or it is disabled"
- Cause
- The required API isn't enabled in the project (each API must be enabled once).
- Fix
- Click the URL in the error, or run:
Wait a few minutes for propagation, then retry.
gcloud services enable SERVICE.googleapis.com - Docs
- service-usage/docs/enable-disable
▸Can't enable an API ("permission denied to enable service")
- Cause
- You lack
serviceusage.services.enable(Service Usage Admin). - Fix
- Get granted
roles/serviceusage.serviceUsageAdmin, or ask an admin to enable it. - Docs
- service-usage/docs/access-control
🔑 Authentication & gcloud CLI
▸Commands run against the wrong project
- Cause
- The gcloud
core/projectproperty points to a different project than intended. - Fix
- Check and set it:
Or add
gcloud config get-value project gcloud config set project PROJECT_ID--project=per command. Use named configurations for multiple projects. - Docs
- sdk/docs/configurations
▸"Could not automatically determine credentials" (ADC missing)
- Cause
- Client libraries use Application Default Credentials, which aren't set up locally.
- Fix
- Run:
This is separate from
gcloud auth application-default logingcloud auth login(which only authenticates the CLI). Set a quota project if prompted. - Docs
- authentication/provide-credentials-adc
▸Did "gcloud auth login" but libraries still fail
- Cause
gcloud auth loginauthenticates the CLI only — not the ADC used by SDKs.- Fix
- Also run
gcloud auth application-default login. Remember the two are different. - Docs
- authentication/troubleshoot-adc
▸Reauthentication / expired credentials on the CLI
- Cause
- Tokens expired, or the org requires periodic reauth.
- Fix
- Run
gcloud auth loginagain. For automation, usegcloud auth activate-service-accountor attached service accounts. - Docs
- sdk/gcloud/reference/auth/login
▸Service-account-key security warnings / leaked keys
- Cause
- Long-lived JSON SA keys are risky and often blocked by org policy.
- Fix
- Prefer Workload Identity Federation, attached service accounts, or impersonation instead of downloaded keys.
- Docs
- best-practices-for-managing-service-account-keys
🖥️ Compute Engine
▸Can't SSH into a Linux VM (timeout / connection refused / permission denied)
- Cause
- Missing firewall rule for TCP 22, SSH keys not propagated, guest environment not running, or OS Login misconfig.
- Fix
- Try the console SSH-in-browser first; ensure an ingress rule allows
tcp:22from your IP (or35.235.240.0/20for IAP); run the built-in SSH troubleshooting tool; check the serial console. - Docs
- troubleshooting-ssh-errors
▸"Permission denied (publickey)" via terminal SSH
- Cause
- Key mismatch, or OS Login is enabled but your account lacks the OS Login role.
- Fix
- Grant
roles/compute.osLogin; or add your SSH key to instance/project metadata if not using OS Login. - Docs
- troubleshoot-os-login
▸VM won't start / stuck / "resources not available in zone"
- Cause
- Zonal stockout for the machine type, or quota exhausted.
- Fix
- Try another zone/region or a different machine type; check quota; review the VM's serial console for boot errors.
- Docs
- troubleshooting-using-serial-console
▸Can't reach the VM's web app from the internet
- Cause
- No external IP, or no firewall rule opening the app port (80/443).
- Fix
- Assign an external IP (or use a load balancer); add an ingress
allowrule for the port and target tags/service account. - Docs
- firewall/docs/using-firewalls
▸Lost external IP after stopping/starting the VM
- Cause
- Ephemeral external IPs are released on stop.
- Fix
- Promote/reserve a static external IP and attach it. Note: reserved-but-unattached IPs are billed.
- Docs
- reserve-static-external-ip-address
▸"No space left on device" / disk full on a running VM
- Cause
- The boot/data disk filled up; unlike some clouds, resizing the disk in the console does not auto-grow the filesystem.
- Fix
- Resize the disk (Compute → Disks → Edit → increase size — this is online), then grow the partition/filesystem inside the guest (
growpart+resize2fs/xfs_growfs). Clean logs/caches to buy time. - Docs
- disks/resize-persistent-disk
▸Metadata / startup script changes didn't take effect
- Cause
- Startup scripts run at boot; editing metadata on a running VM doesn't re-run them.
- Fix
- Reset/reboot the VM to re-run the startup script, or run it manually. Check execution in the serial console output.
- Docs
- instances/startup-scripts/linux
🌐 Networking / VPC
▸"Connection timed out" / "connection refused" between resources
- Cause
- Firewall rule missing/misordered, wrong target tags, service not listening, or wrong port.
- Fix
- Confirm an ingress
allowrule matches source range, protocol, and port and applies to the target; verify the service listens on that port/interface. Use Connectivity Tests to pinpoint. - Docs
- vpc/docs/firewalls · connectivity tests
▸Private VM (no external IP) can't reach the internet (apt/pip timeouts)
- Cause
- VMs without external IPs need Cloud NAT for outbound internet; egress may also be firewalled.
- Fix
- Create a Cloud Router + Cloud NAT gateway for the subnet's region; ensure no egress
denyrule blocks traffic (egress rules apply before NAT). - Docs
- nat/docs/gce-example · troubleshooting
▸VPC Peering set up but resources still can't talk
- Cause
- Peering isn't transitive, subnet CIDRs overlap, or firewall rules don't allow the peer range; both sides must create the peering.
- Fix
- Create peering on both VPCs, ensure non-overlapping subnets, and add firewall rules allowing the peer's ranges.
- Docs
- vpc/docs/vpc-peering
▸Default firewall behavior confusion
- Cause
- VPC default is deny ingress / allow egress; the "default" network's implied allow rules may not exist on custom VPCs.
- Fix
- Explicitly create needed allow rules (allow-ssh, allow-internal, allow-http). Use firewall rule logging to debug.
- Docs
- firewall/docs/firewalls
🪣 Cloud Storage
▸Can't make a bucket/object public ("Public access prevention is enforced")
- Cause
- Public Access Prevention is on (bucket, or inherited from org policy), blocking
allUsers/allAuthenticatedUsersgrants. - Fix
- If policy allows, disable PAP on the bucket, then grant
roles/storage.objectViewertoallUsers. If enforced by org policy, an admin must change it. - Docs
- public-access-prevention · making-data-public
▸"AccessDenied" / 403 on uploads or reads
- Cause
- Principal lacks the right Storage role, or the bucket uses uniform bucket-level access (ACLs disabled).
- Fix
- Grant
roles/storage.objectAdmin/objectViewerat bucket level; with uniform access, use IAM (not object ACLs). - Docs
- access-control/iam · uniform-bucket-level-access
▸Uploads failing / general 4xx–5xx from Storage
- Cause
- Wrong bucket name, missing permission, or CORS/precondition issues for browser uploads.
- Fix
- Follow the Storage troubleshooting matrix; for browser uploads, configure CORS on the bucket.
- Docs
- storage/docs/troubleshooting
▸Accidental public / exposed bucket
- Cause
- Legacy ACLs or
allUsersgrants left data open. - Fix
- Enable uniform bucket-level access + public access prevention; audit with the IAM policy analyzer.
- Docs
- using-public-access-prevention
⚡ Serverless — Cloud Run, Functions, App Engine
▸Cloud Run: "container failed to start and listen on the port defined by the PORT environment variable"
- Cause
- The app doesn't listen on
0.0.0.0:$PORT(hardcoded port, bound to 127.0.0.1, or startup crash/timeout). - Fix
- Read
PORTfrom env and bind to0.0.0.0; test the image locally; check Cloud Logging for the startup stack trace; raise the startup timeout if init is slow. - Docs
- run/docs/troubleshooting · container-contract
▸Cloud Run / Functions: cold starts / slow first request
- Cause
- New instances spin up on demand; heavy init or large images increase latency.
- Fix
- Set
--min-instancesto keep instances warm; slim the image; move heavy init to global scope. (Min instances incur cost.) - Docs
- configuring/min-instances
▸Cloud Run: 403 "Forbidden" when calling the service
- Cause
- Service requires authentication (default) and the caller isn't an invoker.
- Fix
- For public access,
--allow-unauthenticated(grantsroles/run.invokertoallUsers); for service-to-service, grant the calling SAroles/run.invokerand send an ID token. - Docs
- authenticating/public · service-to-service
▸Cloud Functions: deploy fails at build (dependency or entry-point error)
- Cause
- Missing/incorrect entry point name, dependency install failure, or wrong runtime.
- Fix
- Match
--entry-pointto the exported function name; verify the dependency manifest; read the Cloud Build logs linked in the error. - Docs
- functions/docs/troubleshooting
▸Deploy: "Build failed / permission denied" on Cloud Build or Artifact Registry
- Cause
- The Cloud Build or default compute service agent lacks required roles, or the Cloud Build/Artifact Registry APIs aren't enabled.
- Fix
- Enable Cloud Build + Artifact Registry APIs; ensure the build SA has needed roles (Google usually auto-grants; re-grant if removed).
- Docs
- build/docs/troubleshooting
▸App Engine: `gcloud app deploy` fails (region locked / app not created)
- Cause
- An App Engine app must be created once per project and its region can't be changed later.
- Fix
- Run
gcloud app create --region=REGIONbefore the first deploy; pick the region carefully (permanent). - Docs
- appengine/docs/…/creating-project
▸App Engine: 502 / scaling / startup errors after deploy
- Cause
- App not listening on
$PORT/8080, failing health checks, or missing runtime config. - Fix
- Bind to the provided port, verify
app.yaml, and check Logs Explorer for the failing request. - Docs
- appengine/docs/standard/troubleshooting
☸️ Kubernetes / GKE
▸Pod stuck in ImagePullBackOff / ErrImagePull
- Cause
- Wrong image name/tag, or the node/cluster SA can't authenticate to the registry (Artifact Registry).
- Fix
- Verify the image path and tag; grant the node service account
roles/artifactregistry.reader; confirm the image exists in the region. - Docs
- troubleshooting/image-pulls
▸Pod in CrashLoopBackOff
- Cause
- The container starts then exits repeatedly (app error, bad command, missing config/secret, failing liveness probe).
- Fix
- Inspect the crash:
Fix the command/env/probe; check the exit code.
kubectl logs POD --previous - Docs
- troubleshooting/crashloopbackoff-events
▸Pod Pending — "Insufficient cpu/memory" / won't schedule
- Cause
- No node has enough allocatable resources for the pod's requests, or the node pool is too small.
- Fix
- Lower resource requests, scale/enable the node pool, or turn on cluster autoscaling.
- Docs
- troubleshooting/deployed-workloads
▸kubectl can't connect / "You must be logged in" after cluster create
- Cause
- kubeconfig credentials not fetched, or missing the gke-gcloud-auth-plugin.
- Fix
- Fetch credentials and install the plugin:
gcloud container clusters get-credentials CLUSTER --region REGION - Docs
- how-to/cluster-access-for-kubectl
▸Service LoadBalancer stuck in <pending> / no external IP
- Cause
- LB provisioning in progress, external-IP quota, or annotation/subnet misconfig.
- Fix
- Wait a few minutes; check
IN_USE_ADDRESSESquota and events withkubectl describe svc. - Docs
- concepts/service-load-balancer
▸403 / permission denied creating or managing GKE
- Cause
- Missing
roles/container.admin/developer, or the GKE API/service agent isn't set up. - Fix
- Enable the Kubernetes Engine API; grant appropriate container roles.
- Docs
- kubernetes-engine/docs/how-to/iam
🗄️ BigQuery
▸"Not found: Table/Dataset" even though it exists
- Cause
- Wrong project/dataset qualification, or a region mismatch — you can't query a dataset in
USfrom a job running inEU. - Fix
- Fully-qualify as
`project.dataset.table`; confirm the dataset's location and run the job in the same region; check for typos/case (dataset IDs are case-sensitive). - Docs
- bigquery/docs/error-messages
▸Query scanned way more data than expected / surprise cost
- Cause
- On-demand pricing bills by bytes scanned.
SELECT *, no partition filter, or querying a huge table scans everything. - Fix
- Select only needed columns; filter on the partition column; check the estimate in the UI (dry-run bytes) before running; set a Maximum bytes billed limit on the query to hard-cap it.
- Docs
- bigquery/docs/best-practices-costs
▸"Access Denied: BigQuery ... permission bigquery.jobs.create"
- Cause
- To run any query you need job-creation permission in the billing project, plus data access on the dataset — they're separate grants.
- Fix
- Grant
roles/bigquery.jobUseron the project (to run jobs) androles/bigquery.dataVieweron the dataset (to read data).bigquery.usercovers both for own datasets. - Docs
- bigquery/docs/access-control
🛢️ Cloud SQL & databases
▸App / Cloud Run can't connect to Cloud SQL
- Cause
- Trying to reach a private IP with no connectivity, or a public IP with no authorized network — plus the connector service often isn't wired up.
- Fix
- For Cloud Run, add the Cloud SQL connection (
--add-cloudsql-instances) and connect via the Unix socket, or use the Cloud SQL Auth Proxy / connector. For private IP, use a Serverless VPC connector. Grant the runtime SAroles/cloudsql.client. - Docs
- sql/docs/connect-run · auth proxy
▸Cloud SQL: "too many connections" / connection pool exhausted
- Cause
- Serverless scale-out opens many connections; each instance's pool multiplies past the DB's
max_connections. - Fix
- Use a small per-instance pool, cap Cloud Run
--max-instances, raisemax_connectionsvia a database flag, or front the DB with a connection pooler (e.g. PgBouncer). - Docs
- sql/docs/manage-connections
📈 Logging & Monitoring
▸Logs aren't showing up in Logs Explorer
- Cause
- Wrong time range/resource filter, a log exclusion/sink dropping them, or the workload's SA lacks
roles/logging.logWriter. - Fix
- Widen the time range and clear filters; check Logging → Log Router for exclusions; ensure the writing SA has Logs Writer. For VMs, confirm the Ops Agent is installed and running.
- Docs
- logging/docs/view/logs-explorer · log router
▸Alerting policy / uptime check never fires (or fires constantly)
- Cause
- No notification channel attached, threshold/duration misconfigured, or the uptime check can't reach a private endpoint through the firewall.
- Fix
- Attach a notification channel to the policy; tune the condition threshold + duration window; for uptime checks, allow Google's uptime-checker source ranges to the target.
- Docs
- monitoring/alerts · uptime-checks
▸Logging bill unexpectedly high
- Cause
- High-volume logs (data-access audit logs, verbose app logs) ingested beyond the free allotment.
- Fix
- Add exclusion filters in the Log Router for noisy, low-value logs; keep only what you need in
_Default; route long-term logs to a cheaper bucket/BigQuery sink. - Docs
- logging/docs/routing/overview
🧱 Terraform & IaC deploys
▸Terraform apply: "Error 403" or "API not enabled"
- Cause
- The identity Terraform runs as lacks the role for that resource, or the service's API isn't enabled in the project.
- Fix
- Read the exact permission/API in the error; enable the API (
gcloud services enable …or agoogle_project_serviceresource) and grant the running SA the needed role. Re-run after propagation. - Docs
- docs/terraform/best-practices
▸"Error acquiring the state lock" (stale lock)
- Cause
- A previous run crashed and left a lock in the GCS backend, or two runs are racing.
- Fix
- Make sure no other apply is running, then force-unlock with the lock ID from the error:
terraform force-unlock LOCK_ID. Use a GCS backend with per-workspace state to avoid collisions. - Docs
- terraform/store-state (GCS backend)
▸Terraform wants to destroy/recreate a resource unexpectedly
- Cause
- A changed immutable field (name, location) forces replacement, or drift from a manual console change.
- Fix
- Read the plan's
# forces replacementlines; if the resource already exists,terraform importit instead of recreating; uselifecycle { prevent_destroy = true }on critical resources. - Docs
- terraform/import
First-response checklist: 1) confirm identity & project (
gcloud auth list, gcloud config get-value project) · 2) confirm the API is enabled (gcloud services list --enabled) · 3) read the exact error string — GCP names the missing permission/API/quota · 4) check Logs Explorer · 5) wait for propagation before deeper debugging.