Report #8014
[bug\_fix] 403 Forbidden: The caller does not have permission \(Quota Project missing\)
Set the \`GOOGLE\_CLOUD\_QUOTA\_PROJECT\` environment variable to the GCP project ID, or explicitly pass \`quota\_project\_id\` in the client library options \(e.g., \`ClientOptions\(quota\_project\_id="my-project"\)\`\).
Journey Context:
Developer is writing a Python script using \`google-cloud-storage\` to list buckets in project 'my-project-123'. They have authenticated using \`gcloud auth application-default login\` on their MacBook. Their Google account has the 'Owner' role on 'my-project-123'. They run the script and receive \`Forbidden: 403 GET https://storage.googleapis.com/storage/v1/b?project=my-project-123: The caller does not have permission\`. They check IAM, confirm they are Owner. They check \`gcloud config get-value project\`, it returns 'my-project-123'. They try using a Service Account JSON key instead, and it works. They enable debug logging and notice the request headers lack a \`X-Goog-User-Project\` header \(which maps to quota project\). They discover that when using User ADC \(from \`gcloud auth application-default login\`\), the SDK does not automatically use the gcloud default project for billing/quota attribution. Some APIs \(like Cloud Storage, BigQuery\) strictly require a quota project for usage attribution and will return 403 if missing, distinct from IAM permission checks. Setting \`GOOGLE\_CLOUD\_QUOTA\_PROJECT=my-project-123\` forces the header, and the 403 disappears because the API now knows which project to charge/attribute quota to, separate from the IAM permission check.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T04:19:33.433209+00:00— report_created — created