Skip to content

google-vertex provider: "<authenticated>" sentinel passed as API key breaks ADC auth #50053

@vswarm-ai

Description

@vswarm-ai

Bug Description

When using the google-vertex provider with Application Default Credentials (ADC) via a service account, OpenClaw passes the literal string "<authenticated>" as options.apiKey to the pi-ai stream handler, which then tries to use it as an actual API key instead of falling through to ADC.

Root Cause

  1. pi-ai's getEnvApiKey("google-vertex") returns "<authenticated>" as a sentinel value when GOOGLE_APPLICATION_CREDENTIALS + GOOGLE_CLOUD_PROJECT + GOOGLE_CLOUD_LOCATION are all set.
  2. OpenClaw's resolveApiKeyForProvider calls resolveEnvApiKey("google-vertex") → gets { apiKey: "<authenticated>", source: "gcloud adc" }.
  3. This gets passed as options.apiKey to pi-ai's streamGoogleVertex handler.
  4. The handler's resolveApiKey(options) sees a truthy apiKey → calls createClientWithApiKey("<authenticated>") instead of createClient() (which would use ADC).
  5. The @google/genai SDK sends "<authenticated>" as the API key to Vertex AI → 401 CREDENTIALS_MISSING.

Expected Behavior

When ADC credentials are configured for google-vertex, the handler should use new GoogleGenAI({ vertexai: true, project, location }) (the ADC path), not pass a sentinel string as an API key.

Workaround

Two changes are needed:

1. Set auth: "oauth" on the google-vertex provider in openclaw.json:

{
  "google-vertex": {
    "models": [...],
    "baseUrl": "https://aiplatform.googleapis.com",
    "auth": "oauth"
  }
}

2. Patch pi-ai's resolveApiKey in node_modules/@mariozechner/pi-ai/dist/providers/google-vertex.js:

// Original:
function resolveApiKey(options) {
    return options?.apiKey || process.env.GOOGLE_CLOUD_API_KEY;
}

// Patched:
function resolveApiKey(options) {
    const _k = options?.apiKey || process.env.GOOGLE_CLOUD_API_KEY;
    return (_k && !_k.startsWith("<")) ? _k : undefined;
}

Suggested Fix

Either:

  • In OpenClaw: Check if the resolved apiKey is the "<authenticated>" sentinel before passing it to the stream handler. If so, pass undefined instead, or pass a separate flag like { useAdc: true }.
  • In pi-ai: Have streamGoogleVertex check for the sentinel in resolveApiKey and treat it as "no key" (falling through to ADC).
  • Ideally both: The sentinel pattern is fragile. A dedicated auth mode field would be more robust.

Additional Context

  • auth: "oauth" on the provider config is supposed to skip API key resolution (via resolveProviderAuthOverride), but resolveApiKeyForProvider still calls resolveEnvApiKey() even when authOverride === "oauth". The override only blocks aws-sdk and resolveSyntheticLocalProviderAuth.
  • The api field cannot be set to "google-vertex" (schema enum doesn't include it), but omitting it works because OpenClaw infers the handler from the provider name.
  • The "<authenticated>" sentinel is also used for amazon-bedrock — same issue may exist there.

Environment

  • OpenClaw 2026.3.13
  • pi-ai (bundled version)
  • macOS (arm64)
  • Service account auth via GOOGLE_APPLICATION_CREDENTIALS

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions