mastra-ai/mastra · error
Google service account token request failed (${response.stat
Error message
Google service account token request failed (${response.status}): ${await response.text()} What it means
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.
Source
Thrown at auth/google/src/rbac-provider.ts:238
throw new Error(
`Google service account private key signing failed (${(err as Error).message}). ` +
`Key has BEGIN marker: ${hasBegin}, END marker: ${hasEnd}. ` +
`Ensure your .env value contains the raw PEM with \\n for newlines, without extra surrounding quotes or commas.`,
);
}
const response = await fetch(OAUTH_TOKEN_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: `${unsigned}.${signature}`,
}),
signal: AbortSignal.timeout(DEFAULT_FETCH_TIMEOUT_MS),
});
if (!response.ok) {
throw new Error(`Google service account token request failed (${response.status}): ${await response.text()}`);
}
const json = (await response.json()) as { access_token: string; expires_in: number };
this.accessToken = json.access_token;
this.tokenExpiresAt = Date.now() + json.expires_in * 1000;
return json.access_token;
}
private base64Url(value: string): string {
return Buffer.from(value).toString('base64url');
}
private normalizePrivateKey(key: string): string {
let out = key.trim();
for (let i = 0; i < 5; i++) {
const before = out;
if (out.endsWith(',')) out = out.slice(0, -1).trim();View on GitHub (pinned to 75dd419e61)
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
Defensive patterns
Strategy: retry
Validate before calling
// Validate inputs before calling
if (!clientEmail || !privateKey || !clientEmail.endsWith('.iam.gserviceaccount.com')) {
throw new Error('Invalid Google service account credentials');
}
// Check clock skew
const skew = Math.abs(Date.now() - (await getServerTimeUtc()));
if (skew > 60_000) throw new Error('System clock skewed by ' + skew + 'ms'); Try / catch
try {
const token = await provider.getToken();
} catch (err) {
const msg = err instanceof Error ? err.message : '';
if (msg.includes('invalid_grant')) {
// key revoked/expired or clock skew — do NOT retry with same creds
console.error('Check service account key validity and system clock');
} else if (msg.includes('status: 5') || msg.includes('status: 429')) {
await new Promise(r => setTimeout(r, 1000)); // transient — safe to retry
return provider.getToken();
}
throw err;
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Token exchange failed: ${error}
- Failed to fetch user info from Clerk
- Invalid state redirect suffix
- Invalid state token format
- Invalid state token signature
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/6f3e8a5db3d07876.
Report an issue: GitHub.