Bug
google-vertex models using Application Default Credentials (ADC) — via GOOGLE_APPLICATION_CREDENTIALS service account key or gcloud auth application-default login — fail with 401 UNAUTHENTICATED:
API keys are not supported by this API. Expected OAuth2 access token or other authentication credentials that assert a principal.
Root Cause
pi-ai's getEnvApiKey("google-vertex") returns the string "<authenticated>" as a sentinel when ADC environment variables are configured (GOOGLE_APPLICATION_CREDENTIALS + GOOGLE_CLOUD_PROJECT + GOOGLE_CLOUD_LOCATION). This sentinel is meant to signal "auth is available" for model discovery, but it flows into the google-vertex provider as a literal API key through two independent paths:
Path 1: openclaw's resolveEnvApiKey
resolveEnvApiKey("google-vertex") in src/agents/model-auth.ts calls getEnvApiKey("google-vertex") → returns { apiKey: "<authenticated>", source: "gcloud adc" } → resolveApiKeyForProvider returns it with mode: "api-key" → applyApiKeyInfo calls authStorage.setRuntimeApiKey("google-vertex", "<authenticated>").
Path 2: pi-ai's internal AuthStorage.getApiKey fallback
Even without Path 1, pi-coding-agent's AuthStorage.getApiKey("google-vertex") falls back to getEnvApiKey("google-vertex") → returns "<authenticated>" → passed as options.apiKey to the provider.
Both paths end the same way
The google-vertex provider's resolveApiKey(options) returns options.apiKey ("<authenticated>", truthy) → calls createClientWithApiKey(model, "<authenticated>") → sends x-goog-api-key: <authenticated> header → Vertex AI rejects with 401.
The correct path would be resolveApiKey returning undefined → createClient(model, project, location) → uses GoogleAuth with GOOGLE_APPLICATION_CREDENTIALS → real OAuth Bearer token → 200.
Breaking Commits
bce62f8c0f (2026-01-05) — pi-ai upgrade to ^0.36.0 which introduced the "<authenticated>" sentinel in getEnvApiKey("google-vertex")
b04c838c15e (2026-01-06) — feat!: redesign model config + auth profiles which added the resolveEnvApiKey google-vertex block that passes the sentinel through Path 1
Before these commits, getEnvApiKey("google-vertex") returned undefined, so the provider always took the ADC path.
Environment
- Node.js 25.6.1
@mariozechner/pi-ai 0.58.0
@mariozechner/pi-coding-agent 0.58.0
- macOS with launchd-managed gateway
GOOGLE_APPLICATION_CREDENTIALS pointing to a valid service account JSON key
Ideal upstream fix
The root fix belongs in @mariozechner/pi-ai: getEnvApiKey should not return "<authenticated>" from AuthStorage.getApiKey (or the google-vertex provider should recognize and strip the sentinel). Until then, openclaw needs a workaround.
Bug
google-vertex models using Application Default Credentials (ADC) — via
GOOGLE_APPLICATION_CREDENTIALSservice account key orgcloud auth application-default login— fail with 401 UNAUTHENTICATED:Root Cause
pi-ai's
getEnvApiKey("google-vertex")returns the string"<authenticated>"as a sentinel when ADC environment variables are configured (GOOGLE_APPLICATION_CREDENTIALS+GOOGLE_CLOUD_PROJECT+GOOGLE_CLOUD_LOCATION). This sentinel is meant to signal "auth is available" for model discovery, but it flows into the google-vertex provider as a literal API key through two independent paths:Path 1: openclaw's
resolveEnvApiKeyresolveEnvApiKey("google-vertex")insrc/agents/model-auth.tscallsgetEnvApiKey("google-vertex")→ returns{ apiKey: "<authenticated>", source: "gcloud adc" }→resolveApiKeyForProviderreturns it withmode: "api-key"→applyApiKeyInfocallsauthStorage.setRuntimeApiKey("google-vertex", "<authenticated>").Path 2: pi-ai's internal
AuthStorage.getApiKeyfallbackEven without Path 1, pi-coding-agent's
AuthStorage.getApiKey("google-vertex")falls back togetEnvApiKey("google-vertex")→ returns"<authenticated>"→ passed asoptions.apiKeyto the provider.Both paths end the same way
The google-vertex provider's
resolveApiKey(options)returnsoptions.apiKey("<authenticated>", truthy) → callscreateClientWithApiKey(model, "<authenticated>")→ sendsx-goog-api-key: <authenticated>header → Vertex AI rejects with 401.The correct path would be
resolveApiKeyreturningundefined→createClient(model, project, location)→ usesGoogleAuthwithGOOGLE_APPLICATION_CREDENTIALS→ real OAuth Bearer token → 200.Breaking Commits
bce62f8c0f(2026-01-05) — pi-ai upgrade to ^0.36.0 which introduced the"<authenticated>"sentinel ingetEnvApiKey("google-vertex")b04c838c15e(2026-01-06) —feat!: redesign model config + auth profileswhich added theresolveEnvApiKeygoogle-vertex block that passes the sentinel through Path 1Before these commits,
getEnvApiKey("google-vertex")returnedundefined, so the provider always took the ADC path.Environment
@mariozechner/pi-ai0.58.0@mariozechner/pi-coding-agent0.58.0GOOGLE_APPLICATION_CREDENTIALSpointing to a valid service account JSON keyIdeal upstream fix
The root fix belongs in
@mariozechner/pi-ai:getEnvApiKeyshould not return"<authenticated>"fromAuthStorage.getApiKey(or the google-vertex provider should recognize and strip the sentinel). Until then, openclaw needs a workaround.