{"record":{"id":"97b98bb96da244e5","repo":"decolua/9router","slug":"vertex-failed-to-refresh-access-token-from-adc-js","errorCode":null,"errorMessage":"Vertex: failed to refresh access token from ADC JSON (authorized_user)","messagePattern":"Vertex: failed to refresh access token from ADC JSON \\(authorized_user\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/vertex.js","lineNumber":150,"sourceCode":"    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);\n      if (!projectId) throw new Error(\"Vertex: could not resolve project_id from API key. Please add it manually in provider settings.\");\n      log?.debug?.(\"VERTEX\", `Resolved project_id: ${projectId}`);\n      credentials.providerSpecificData = { ...credentials.providerSpecificData, projectId };\n    }\n\n    const url = this.buildUrl(model, stream, 0, credentials);\n    const headers = this.buildHeaders(credentials, stream);\n    const transformedBody = this.transformRequest(model, body, stream, credentials);\n\n    const response = await proxyAwareFetch(url, {\n      method: \"POST\",\n      headers,","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/vertex.js#L132-L168","documentation":"Thrown in VertexExecutor.execute (open-sse/executors/vertex.js:150) when the ADC (Application Default Credentials) authorized_user flow fails: refreshGoogleToken(refresh_token, client_id, client_secret) returned no accessToken. ADC JSON created by `gcloud auth application-default login` holds a long-lived refresh token that is exchanged at Google's OAuth2 token endpoint; a failed exchange yields no Bearer token.","triggerScenarios":"credentials.apiKey parses as ADC JSON (type 'authorized_user' with client_id, client_secret, refresh_token) and refreshGoogleToken resolves to null or { accessToken: undefined } — typically because Google rejected the refresh_token (invalid_grant) or the token endpoint was unreachable.","commonSituations":"The ADC refresh token was revoked (running `gcloud auth application-default revoke`, password change, or >7 days of inactivity for certain scopes); the ADC JSON is stale from a previous machine; client_id/client_secret were edited; corporate proxy blocks oauth2.googleapis.com.","solutions":["Re-run `gcloud auth application-default login` to obtain a fresh refresh_token, then re-save the ADC JSON as the apiKey.","Run `gcloud auth application-default set-quota-project YOUR_PROJECT_ID` in the same session so the new JSON is complete.","Verify network/proxy access to https://oauth2.googleapis.com/token.","If ADC keeps failing, switch to a Service Account JSON key instead (different minting path, not user-token dependent)."],"exampleFix":"// before: stale ADC JSON\n{ \"type\": \"authorized_user\", \"refresh_token\": \"1//old...\", ... }\n// after: regenerate, then\n{ \"type\": \"authorized_user\", \"refresh_token\": \"1//new...\", \"client_id\": \"...\", \"client_secret\": \"...\", \"quota_project_id\": \"my-gcp-project\" }","handlingStrategy":"retry","validationCode":"const adc = parseVertexAdcJson(creds?.apiKey);\nif (adc) {\n  const required = [\"refresh_token\", \"client_id\", \"client_secret\"];\n  const missing = required.filter(k => !adc[k]);\n  if (missing.length) throw new Error(`ADC JSON missing fields: ${missing.join(\", \")}`);\n}","typeGuard":"function isValidAdcJson(v) {\n  if (typeof v !== \"string\") return false;\n  try {\n    const j = JSON.parse(v);\n    return j.type === \"authorized_user\" && !!j.refresh_token && !!j.client_id && !!j.client_secret;\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  await vertexChat(model, body);\n} catch (e) {\n  if (/failed to refresh access token from ADC JSON/.test(e.message)) {\n    // refresh token likely revoked — cannot self-heal, force re-login\n    await gcloudAdcReLoginFlow();\n  } else throw e;\n}","preventionTips":["Refresh ADC tokens periodically — long-idle refresh tokens can be revoked.","Avoid running `gcloud auth application-default revoke`; it invalidates the stored token.","Detect invalid_grant early and prompt re-login instead of retrying endlessly.","Have a fallback SA-JSON credential path for headless environments."],"tags":["auth","oauth","google-cloud","refresh-token","adc"],"backgroundTag":"oauth-refresh-token-rejected","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}