Report #51938
[bug\_fix] invalid\_grant: Token has been expired or revoked \(GCP Service Account\)
This OAuth2 error occurs during service account authentication when: \(1\) The service account key JSON file being used was deleted or deactivated in the GCP Console after download \(the key ID no longer exists\). \(2\) The system clock is skewed by more than a few minutes, causing the JWT 'iat' \(issued at\) claim to be in the future relative to Google's servers, or the 'exp' \(expiry\) to exceed the 1-hour maximum allowed skew. \(3\) The service account itself was deleted. The fix is to verify the key exists in IAM > Service Accounts \(regenerate if missing\), ensure system NTP sync is active, or recreate the service account if deleted.
Journey Context:
Developer has a CI/CD pipeline on GitLab CI that uses a GCP service account key stored as a CI variable to push artifacts to GCS. The pipeline suddenly fails with 'invalid\_grant' after running fine for months. The developer checks the key JSON stored in the CI variable: it is valid JSON with client\_email and private\_key. They try to manually request a token using \`curl\` with a JWT they generate, and get the same 'invalid\_grant' response. They log into the GCP Console, navigate to IAM > Service Accounts, find the specific service account, and click the 'Keys' tab. The key ID listed in the JSON file is not present in the list—it was deleted yesterday by another team member during a security audit. They click 'Add Key' > 'Create new key', download the new JSON, update the GitLab CI variable with the new key contents, and the pipeline succeeds. If the key had existed, they would have checked the runner's VM clock and found it 5 minutes fast, causing the JWT 'iat' claim to be in the future relative to Google; syncing NTP would have fixed it.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T17:40:17.687035+00:00— report_created — created