Bug Description
Google Vertex AI with Application Default Credentials (ADC) fails with 401 UNAUTHENTICATED since OpenClaw 2026.3.13 / pi-ai 0.58.0 / @google/genai 1.45.0.
The <authenticated> sentinel value returned by pi-ai's getEnvApiKey("google-vertex") is passed as a literal API key to the @google/genai SDK, which bypasses ADC token exchange and sends the string <authenticated> as an x-goog-api-key header to aiplatform.googleapis.com.
Root Cause
Two layers contribute to the bug:
Layer 1: pi-ai env-api-keys.js
When GOOGLE_APPLICATION_CREDENTIALS + GOOGLE_CLOUD_PROJECT + GOOGLE_CLOUD_LOCATION are all set, getEnvApiKey("google-vertex") returns the marker string "<authenticated>":
// pi-ai/dist/env-api-keys.js
if (hasCredentials && hasProject && hasLocation) {
return "<authenticated>";
}
Layer 2: OpenClaw reply-*.js — resolveEnvApiKey()
OpenClaw calls getEnvApiKey() and blindly passes the result as apiKey:
// openclaw/dist/reply-Bm8VrLQh.js line ~36942
if (normalized === "google-vertex") {
const envKey = getEnvApiKey(normalized); // returns "<authenticated>"
if (!envKey) return null;
return {
apiKey: envKey, // "<authenticated>" treated as a real key!
source: "gcloud adc"
};
}
Layer 3: pi-ai google-vertex.js
The correctly-implemented google-vertex.js provider checks options.apiKey:
function resolveApiKey(options) {
return options?.apiKey || process.env.GOOGLE_CLOUD_API_KEY;
}
const client = apiKey
? createClientWithApiKey(model, apiKey, ...) // ← takes this path with "<authenticated>"!
: createClient(model, project, location, ...); // ← should take this ADC path
Since options.apiKey is "<authenticated>" (truthy), it uses createClientWithApiKey() which passes apiKey: "<authenticated>" to new GoogleGenAI({ vertexai: true, apiKey: "<authenticated>" }).
Layer 4: @google/genai SDK 1.45.0
The SDK constructor sees apiKey is set and clears project/location:
if ((envProject || envLocation) && options.apiKey) {
console.debug('The user provided Vertex AI API key will take precedence...');
this.project = undefined;
this.location = undefined;
}
Then sends <authenticated> as x-goog-api-key header → Google returns 401.
Expected Behavior
When ADC is configured (service account JSON + project + location), OpenClaw should:
- NOT pass
"<authenticated>" as apiKey to pi-ai stream options
- Let
google-vertex.js's resolveApiKey() return undefined
- Which correctly invokes
createClient() with vertexai: true + project + location
Suggested Fix
In resolveEnvApiKey() (OpenClaw side), when the provider is google-vertex and the env key is the <authenticated> marker, return null (or omit apiKey) so the pi-ai google-vertex provider falls through to the ADC path:
if (normalized === "google-vertex") {
const envKey = getEnvApiKey(normalized);
if (!envKey) return null;
// Don't pass the marker as an actual apiKey
if (envKey === "<authenticated>") {
return { apiKey: undefined, source: "gcloud adc" };
}
return { apiKey: envKey, source: "gcloud adc" };
}
Alternatively, pi-ai's google-vertex.js should filter out the <authenticated> marker in resolveApiKey().
Environment
- OpenClaw: 2026.3.13 (61d171a)
- pi-ai: 0.58.0
- @google/genai: 1.45.0
- Node.js: v24.14.0
- OS: macOS (darwin)
- Auth: Service account JSON via
GOOGLE_APPLICATION_CREDENTIALS
- gcloud ADC: Working (
gcloud auth application-default print-access-token succeeds)
Reproduction
- Configure Google Vertex AI with service account ADC:
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
GOOGLE_CLOUD_PROJECT=my-project
GOOGLE_CLOUD_LOCATION=global
- Set default model to
google-vertex/gemini-3-flash-preview
- Run
openclaw models status --probe
- Observe 401: "API keys are not supported by this API. Expected OAuth2 access token"
- The warning
"The user provided Vertex AI API key will take precedence" confirms the SDK received an API key instead of ADC credentials
Bug Description
Google Vertex AI with Application Default Credentials (ADC) fails with 401 UNAUTHENTICATED since OpenClaw 2026.3.13 / pi-ai 0.58.0 / @google/genai 1.45.0.
The
<authenticated>sentinel value returned bypi-ai'sgetEnvApiKey("google-vertex")is passed as a literal API key to the@google/genaiSDK, which bypasses ADC token exchange and sends the string<authenticated>as anx-goog-api-keyheader toaiplatform.googleapis.com.Root Cause
Two layers contribute to the bug:
Layer 1: pi-ai
env-api-keys.jsWhen
GOOGLE_APPLICATION_CREDENTIALS+GOOGLE_CLOUD_PROJECT+GOOGLE_CLOUD_LOCATIONare all set,getEnvApiKey("google-vertex")returns the marker string"<authenticated>":Layer 2: OpenClaw
reply-*.js—resolveEnvApiKey()OpenClaw calls
getEnvApiKey()and blindly passes the result asapiKey:Layer 3: pi-ai
google-vertex.jsThe correctly-implemented
google-vertex.jsprovider checksoptions.apiKey:Since
options.apiKeyis"<authenticated>"(truthy), it usescreateClientWithApiKey()which passesapiKey: "<authenticated>"tonew GoogleGenAI({ vertexai: true, apiKey: "<authenticated>" }).Layer 4: @google/genai SDK 1.45.0
The SDK constructor sees
apiKeyis set and clearsproject/location:Then sends
<authenticated>asx-goog-api-keyheader → Google returns 401.Expected Behavior
When ADC is configured (service account JSON + project + location), OpenClaw should:
"<authenticated>"asapiKeyto pi-ai stream optionsgoogle-vertex.js'sresolveApiKey()returnundefinedcreateClient()withvertexai: true+project+locationSuggested Fix
In
resolveEnvApiKey()(OpenClaw side), when the provider isgoogle-vertexand the env key is the<authenticated>marker, returnnull(or omitapiKey) so the pi-ai google-vertex provider falls through to the ADC path:Alternatively,
pi-ai'sgoogle-vertex.jsshould filter out the<authenticated>marker inresolveApiKey().Environment
GOOGLE_APPLICATION_CREDENTIALSgcloud auth application-default print-access-tokensucceeds)Reproduction
google-vertex/gemini-3-flash-previewopenclaw models status --probe"The user provided Vertex AI API key will take precedence"confirms the SDK received an API key instead of ADC credentials