{"record":{"id":"6f3e8a5db3d07876","repo":"mastra-ai/mastra","slug":"google-service-account-token-request-failed-res","errorCode":null,"errorMessage":"Google service account token request failed (${response.status}): ${await response.text()}","messagePattern":"Google service account token request failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/google/src/rbac-provider.ts","lineNumber":238,"sourceCode":"      throw new Error(\n        `Google service account private key signing failed (${(err as Error).message}). ` +\n          `Key has BEGIN marker: ${hasBegin}, END marker: ${hasEnd}. ` +\n          `Ensure your .env value contains the raw PEM with \\\\n for newlines, without extra surrounding quotes or commas.`,\n      );\n    }\n\n    const response = await fetch(OAUTH_TOKEN_URL, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n      body: new URLSearchParams({\n        grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n        assertion: `${unsigned}.${signature}`,\n      }),\n      signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT_MS),\n    });\n\n    if (!response.ok) {\n      throw new Error(`Google service account token request failed (${response.status}): ${await response.text()}`);\n    }\n\n    const json = (await response.json()) as { access_token: string; expires_in: number };\n    this.accessToken = json.access_token;\n    this.tokenExpiresAt = Date.now() + json.expires_in * 1000;\n    return json.access_token;\n  }\n\n  private base64Url(value: string): string {\n    return Buffer.from(value).toString('base64url');\n  }\n\n  private normalizePrivateKey(key: string): string {\n    let out = key.trim();\n\n    for (let i = 0; i < 5; i++) {\n      const before = out;\n      if (out.endsWith(',')) out = out.slice(0, -1).trim();","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/google/src/rbac-provider.ts#L220-L256","documentation":"Thrown when the HTTPS request to Google's OAuth2 token endpoint (https://oauth2.googleapis.com/token) returns a non-OK status. The response status and body are included, and the body typically contains Google's error description (e.g. invalid_grant, invalid_scope, invalid_client). This means the client id, key, scopes, or clock are wrong — the token was never issued.","triggerScenarios":"getServiceAccountToken() performs a fetch of the signed JWT assertion (grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer) to Google's token endpoint; any 4xx/5xx response (most commonly 400 invalid_grant) triggers this error.","commonSituations":"Expired or revoked service account key; system clock skew greater than a few minutes; wrong client_email paired with the private key; the service account being disabled or lacking the requested scopes; network proxies returning error bodies.","solutions":["Read the included response body — Google's error (e.g. 'invalid_grant: Invalid JWT Signature') pinpoints the cause","Verify the service account client_email and private_key are from the same JSON key file and the key is still active in Google Cloud Console","Synchronize the machine clock (NTP) — JWT issued_at/expired_at skew causes invalid_grant","Confirm requested scopes are valid and the service account is enabled and not rate-limited"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Validate inputs before calling\nif (!clientEmail || !privateKey || !clientEmail.endsWith('.iam.gserviceaccount.com')) {\n  throw new Error('Invalid Google service account credentials');\n}\n// Check clock skew\nconst skew = Math.abs(Date.now() - (await getServerTimeUtc()));\nif (skew > 60_000) throw new Error('System clock skewed by ' + skew + 'ms');","typeGuard":null,"tryCatchPattern":"try {\n  const token = await provider.getToken();\n} catch (err) {\n  const msg = err instanceof Error ? err.message : '';\n  if (msg.includes('invalid_grant')) {\n    // key revoked/expired or clock skew — do NOT retry with same creds\n    console.error('Check service account key validity and system clock');\n  } else if (msg.includes('status: 5') || msg.includes('status: 429')) {\n    await new Promise(r => setTimeout(r, 1000)); // transient — safe to retry\n    return provider.getToken();\n  }\n  throw err;\n}","preventionTips":["Monitor service account key age and rotate before expiry","Run NTP time sync on all hosts issuing JWTs","Read the response body included in the error — Google's error code identifies the fix","Distinguish 4xx (config, no retry) from 5xx/429 (transient, retry with backoff)"],"tags":["auth","google","oauth","network"],"backgroundTag":"oauth-token-request-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}