{"record":{"id":"551837bb96ed5d48","repo":"decolua/9router","slug":"vertex-oauth-adc-requires-a-project-id-add-quota","errorCode":null,"errorMessage":"Vertex OAuth/ADC requires a project_id. Add quota_project_id to your ADC JSON or set providerSpecificData.projectId.","messagePattern":"Vertex OAuth/ADC requires a project_id\\. Add quota_project_id to your ADC JSON or set providerSpecificData\\.projectId\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/vertex.js","lineNumber":89,"sourceCode":"    const projectId =\n      saJson?.project_id ||\n      adcJson?.quota_project_id ||\n      credentials?.providerSpecificData?.projectId;\n\n    if (this.provider === \"vertex-partner\") {\n      // Partner models require project_id in path regardless of auth method\n      if (!projectId) throw new Error(\"Vertex partner models require a project_id. Add it in providerSpecificData or use Service Account JSON.\");\n      const url = `https://aiplatform.googleapis.com/v1/projects/${projectId}/locations/global/endpoints/openapi/chat/completions`;\n      return rawKey ? `${url}?key=${rawKey}` : url;\n    }\n\n    // Gemini on Vertex\n    const action = stream ? \"streamGenerateContent\" : \"generateContent\";\n\n    if (usesOAuth) {\n      // SA JSON / ADC / pre-set accessToken: must use project-scoped path to avoid RESOURCE_PROJECT_INVALID\n      if (!projectId) {\n        throw new Error(\n          \"Vertex OAuth/ADC requires a project_id. \" +\n          \"Add quota_project_id to your ADC JSON or set providerSpecificData.projectId.\"\n        );\n      }\n      const location = credentials?.providerSpecificData?.location || \"us-central1\";\n      let url = `https://aiplatform.googleapis.com/v1/projects/${projectId}/locations/${location}/publishers/google/models/${model}:${action}`;\n      if (stream) url += \"?alt=sse\";\n      return url;\n    }\n\n    // Raw API key: use global publishers endpoint with ?key= param\n    // ?alt=sse is required for proper SSE streaming (matches every other Gemini executor)\n    let url = `https://aiplatform.googleapis.com/v1/publishers/google/models/${model}:${action}`;\n    if (stream) url += \"?alt=sse\";\n    if (rawKey) url += stream ? `&key=${rawKey}` : `?key=${rawKey}`;\n    return url;\n  }\n","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/vertex.js#L71-L107","documentation":"Thrown by VertexExecutor.buildUrl (open-sse/executors/vertex.js:89) when Gemini on Vertex is requested with OAuth-style credentials (SA JSON, ADC authorized_user JSON, or a pre-set accessToken) but no project_id could be determined. OAuth flows must use the project-scoped path /projects/{id}/locations/{loc}/publishers/google/models/... to avoid Google's RESOURCE_PROJECT_INVALID error, so the executor fails fast with an actionable message instead.","triggerScenarios":"buildUrl is called with provider 'vertex', usesOAuth true (apiKey parsed as SA JSON, parsed as ADC authorized_user JSON, or credentials.accessToken already set), and all of saJson.project_id, adcJson.quota_project_id, and credentials.providerSpecificData.projectId are falsy.","commonSituations":"User pasted ADC credentials from `gcloud auth application-default login` on a machine where no quota project was configured; an accessToken was set manually (e.g. from `gcloud auth print-access-token`) without any projectId in providerSpecificData; a service-account JSON from an unusual source omitted project_id.","solutions":["Set providerSpecificData.projectId in the Vertex connection settings to your GCP project ID.","For ADC credentials run `gcloud auth application-default set-quota-project YOUR_PROJECT_ID`, then re-save the ADC JSON so quota_project_id is present.","Use a full Service Account JSON key (contains project_id) instead of a bare accessToken.","If passing accessToken programmatically, also pass providerSpecificData: { projectId: '...' } in the same credentials object."],"exampleFix":"// before\nconst creds = { accessToken: token };\n// after\nconst creds = { accessToken: token, providerSpecificData: { projectId: \"my-gcp-project\", location: \"us-central1\" } };","handlingStrategy":"validation","validationCode":"const sa = parseVertexSaJson(creds?.apiKey);\nconst adc = parseVertexAdcJson(creds?.apiKey);\nconst usesOAuth = !!sa || !!adc || !!creds?.accessToken;\nconst projectId = sa?.project_id || adc?.quota_project_id || creds?.providerSpecificData?.projectId;\nif (usesOAuth && !projectId) {\n  throw new Error(\"Vertex OAuth credentials need a project_id: set providerSpecificData.projectId or quota_project_id in the ADC JSON\");\n}","typeGuard":"function hasVertexOAuthProjectId(creds) {\n  return Boolean(creds?.providerSpecificData?.projectId) || Boolean(parseVertexSaJson(creds?.apiKey)?.project_id) || Boolean(parseVertexAdcJson(creds?.apiKey)?.quota_project_id);\n}","tryCatchPattern":"try {\n  await chat(model, body);\n} catch (e) {\n  if (/Vertex OAuth\\/ADC requires a project_id/.test(e.message)) {\n    await configureVertexProject(connectionId);\n  } else throw e;\n}","preventionTips":["After `gcloud auth application-default login`, always run `gcloud auth application-default set-quota-project YOUR_PROJECT`.","Never pass a bare accessToken without also passing providerSpecificData.projectId.","Prefer full Service Account JSON over manually minted tokens.","Validate OAuth credentials include a project_id when saving the connection."],"tags":["config","vertex-ai","oauth","missing-project-id"],"backgroundTag":"missing-required-config-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}