{"record":{"id":"6980021108531b1c","repo":"decolua/9router","slug":"vertex-failed-to-mint-access-token-from-service-a","errorCode":null,"errorMessage":"Vertex: failed to mint access token from Service Account JSON","messagePattern":"Vertex: failed to mint access token from Service Account JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/vertex.js","lineNumber":138,"sourceCode":"\n  async refreshCredentials(credentials, log) {\n    const saJson = parseVertexSaJson(credentials?.apiKey);\n    if (!saJson) return null;\n\n    const result = await refreshVertexToken(saJson, log);\n    if (!result) return null;\n\n    return { accessToken: result.accessToken, expiresAt: result.expiresAt };\n  }\n\n  async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {\n    const saJson = parseVertexSaJson(credentials?.apiKey);\n    const adcJson = parseVertexAdcJson(credentials?.apiKey);\n\n    // SA JSON flow: mint Bearer token via JWT assertion (cached)\n    if (saJson) {\n      const result = await refreshVertexToken(saJson, log);\n      if (!result?.accessToken) throw new Error(\"Vertex: failed to mint access token from Service Account JSON\");\n      credentials.accessToken = result.accessToken;\n    }\n\n    // ADC user credential flow: refresh Bearer token via Google OAuth2 token endpoint\n    if (adcJson) {\n      const result = await refreshGoogleToken(\n        adcJson.refresh_token,\n        adcJson.client_id,\n        adcJson.client_secret,\n        log\n      );\n      if (!result?.accessToken) throw new Error(\"Vertex: failed to refresh access token from ADC JSON (authorized_user)\");\n      credentials.accessToken = result.accessToken;\n    }\n\n    // vertex-partner with raw key: auto-resolve project_id if not provided\n    if (this.provider === \"vertex-partner\" && !saJson && !adcJson && !credentials?.providerSpecificData?.projectId) {\n      const projectId = await resolveProjectId(credentials.apiKey);","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/vertex.js#L120-L156","documentation":"Thrown in VertexExecutor.execute (open-sse/executors/vertex.js:138) when refreshVertexToken fails to mint a Bearer access token from the stored Service Account JSON. Token minting (JWT assertion signed with the SA private key, exchanged at Google's OAuth2 token endpoint) is delegated to tokenRefresh.js; a null/empty accessToken result means the exchange did not produce a usable token.","triggerScenarios":"execute() is called, credentials.apiKey parses as a Service Account JSON (parseVertexSaJson), refreshVertexToken(saJson, log) resolves to null or an object without accessToken — e.g. JWT signing failed (bad private_key), Google's token endpoint rejected the assertion (invalid_grant), the network call failed, or the SA key was revoked/disabled.","commonSituations":"Service account key was deleted or disabled in GCP IAM; the SA JSON was copied incompletely (truncated private_key, missing client_email); system clock skew invalidates the JWT iat/exp; outbound access to oauth2.googleapis.com is blocked by firewall/proxy; key file was re-encoded and newlines in private_key were mangled.","solutions":["Re-download a fresh Service Account JSON key from GCP IAM (Keys → Add key → JSON) and re-save it as the credential's apiKey, keeping the private_key newlines intact.","Check connectivity/proxy to https://oauth2.googleapis.com/token — the mint request must reach Google.","Verify the service account is enabled and the key is not expired/revoked in the GCP console.","Check logs from refreshVertexToken (the log object passed in) for the underlying Google error (e.g. invalid_grant, invalid_scope) and fix accordingly."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const sa = parseVertexSaJson(creds?.apiKey);\nif (sa) {\n  const required = [\"client_email\", \"private_key\", \"project_id\"];\n  const missing = required.filter(k => !sa[k]);\n  if (missing.length) throw new Error(`SA JSON missing fields: ${missing.join(\", \")}`);\n}","typeGuard":"function isValidSaJson(v) {\n  if (typeof v !== \"string\") return false;\n  try {\n    const j = JSON.parse(v);\n    return j.type === \"service_account\" && typeof j.private_key === \"string\" && j.private_key.includes(\"PRIVATE KEY\") && !!j.client_email;\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  await vertexChat(model, body);\n} catch (e) {\n  if (/failed to mint access token from Service Account JSON/.test(e.message)) {\n    log.error(\"SA token mint failed — check key validity, clock skew, and oauth2.googleapis.com reachability\");\n    await reauthFlow();\n  } else throw e;\n}","preventionTips":["Re-download SA keys before expiry and rotate them proactively.","Keep the SA JSON intact (preserve private_key newlines) when pasting/storing.","Ensure server clocks are NTP-synced — skew breaks JWT assertions.","Allow egress to oauth2.googleapis.com in firewalls/proxies."],"tags":["auth","oauth","google-cloud","token-minting","service-account"],"backgroundTag":"jwt-token-mint-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}