Skip to content

Fix: fail fast when a Vertex AI Express Mode API key cannot be used - #563

Open
AmaadMartin wants to merge 8 commits into
google:mainfrom
AmaadMartin:fix/vertex-ai-express-mode-api-key-dropped
Open

Fix: fail fast when a Vertex AI Express Mode API key cannot be used#563
AmaadMartin wants to merge 8 commits into
google:mainfrom
AmaadMartin:fix/vertex-ai-express-mode-api-key-dropped

Conversation

@AmaadMartin

Copy link
Copy Markdown
Collaborator

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

N/A — no existing public issue.

2. Or, if no issue exists, describe the change:

Problem:

VertexAiSessionService and VertexAiMemoryBankService resolve a Vertex AI Express Mode API key in their constructors and then throw it away. Both call getExpressModeApiKey(...) and store the result, and the session service even accepts that key in place of projectId + location, but neither ever reads this.expressModeApiKey again: they build new Client({project, location}) and the key is never sent.

The key cannot be sent, because the Agent Engine client has nowhere to put it. @google-cloud/vertexai is pinned at ^1.12.0 (core/package.json, locked to 1.12.0), and node_modules/@google-cloud/vertexai/build/src/genai/client.d.ts declares:

export declare class Client {
  constructor(options: {
    project?: string;
    location?: string;
    apiEndpoint?: string;
  });
}

There is no apiKey option, and 1.12.0 is the latest published version, so a dependency bump does not unblock it either. (This is where adk-js diverges from adk-python, whose vertexai.Client does accept api_key=.)

What a user actually saw before this change, with GOOGLE_GENAI_USE_VERTEXAI=true and GOOGLE_API_KEY set:

  • no projectId/location — construction failed inside the SDK with Authentication is not set up. Please provide either a project and location, or an API key, or a custom base URL., i.e. it asked for an API key the user had already supplied and ADK had already accepted.
  • projectId but no location — construction succeeded silently. The underlying ApiClient defaults location to 'global' and authenticates with Application Default Credentials, so the express key was dropped with no error at all.

Solution:

Fail fast with an actionable message instead of dropping the credential. One shared constant, EXPRESS_MODE_UNSUPPORTED_MESSAGE, lives in the internal core/src/utils/vertex_ai_utils.ts (not added to any public barrel) and is thrown by both constructors when an express-mode key is in effect and the service has to build its own client and there is no usable projectId + location fallback. The message names the cause and both supported ways forward:

Vertex AI Express Mode (expressModeApiKey / GOOGLE_API_KEY) is not supported: the @google-cloud/vertexai Agent Engine client cannot send an API key. Provide projectId and location (with Application Default Credentials), or inject a preconfigured client.

Testing Plan

Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Added an express mode suite to both service test files, covering: the expressModeApiKey option, an API key resolved from GOOGLE_API_KEY, a key alongside a project but no location, the projectId + location ADC path still building the client with exactly {project, location}, and injected sessions/client never building a client at all. Both suites mock the Agent Engine Client (each service imports it from a different specifier) and stub the environment with vi.stubEnv so the cases are hermetic. Every new line and branch is covered.

npx vitest run --project unit:core core/test/sessions/vertex_ai_session_service_test.ts \
  core/test/memory/vertex_ai_memory_bank_service_test.ts core/test/utils/vertex_ai_utils_test.ts
#  Test Files  3 passed (3)   Tests  97 passed (97)

npx vitest run --project unit:core core/test/sessions/registry_test.ts
#  Test Files  1 passed (1)

npx vitest run --project integration tests/integration/memory/vertex_ai_memory_bank_service_test.ts
#  Test Files  1 passed (1)

npm run build     # clean
npm run lint      # clean
npm run format:check   # clean

Manual End-to-End (E2E) Tests:

Please provide instructions on how to manually test your changes, including any necessary setup or configuration.

No automated e2e test is possible: reaching this path requires an express-mode API key against a live Agent Engine, and the point of the change is that such a call cannot be authenticated by the current SDK. The construction behavior was verified manually against the real built package (no mocks):

npm install && npm run build

GOOGLE_GENAI_USE_VERTEXAI=true GOOGLE_API_KEY=<express-mode-api-key> node -e "
const {VertexAiSessionService} = require('./core/dist/cjs/index.js');
const {VertexAiMemoryBankService} = require('./core/dist/cjs/common.js');
new VertexAiSessionService({agentEngineId: '12345'});                    // throws the express-mode message
new VertexAiSessionService({agentEngineId: '12345', projectId: 'p'});    // throws (silently constructed before)
new VertexAiMemoryBankService({agentEngineId: '12345'});                 // throws the express-mode message
new VertexAiMemoryBankService({agentEngineId: '12345', projectId: 'p'}); // throws (silently constructed before)
"

# unchanged, still constructs with ADC even with an API key in the environment:
GOOGLE_GENAI_USE_VERTEXAI=true GOOGLE_API_KEY=<express-mode-api-key> node -e "
const {VertexAiSessionService} = require('./core/dist/cjs/index.js');
new VertexAiSessionService({agentEngineId: '12345', projectId: 'p', location: 'us-central1'});
"

# clean environment, unchanged apart from the reworded message:
node -e "
const {VertexAiSessionService} = require('./core/dist/cjs/index.js');
new VertexAiSessionService({});   // Error: Project ID and Location are required.
"

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

Notes for reviewers:

  • No working configuration regresses. When projectId and location are both present the behavior is unchanged — new Client({project, location}) with ADC — even if GOOGLE_API_KEY happens to be set. This is a deliberate divergence from adk-python, which would prefer the env-derived key; since JS cannot use the key at all, falling back to the working ADC path beats throwing.
  • Injected sessions / client still bypass every guard, so the existing test seams are untouched.
  • The session service's fallback message changed from 'Either (Project ID and Location) or an expressModeApiKey is required.' to 'Project ID and Location are required.'. Keeping the old text would have sent users straight into the error this PR adds, since an expressModeApiKey is never a working alternative for these services.
  • The two guards are shaped differently on purpose. The session service already threw when project/location were missing, so it keeps that guard and only picks a different message. The memory service never threw there, and adding a general required-guard for the non-express path would be an unrelated behavior change, so its guard is scoped to the express-mode case.
  • expressModeApiKey is intentionally kept on both options interfaces, and getExpressModeApiKey's validation is unchanged — the option becomes functional the day the SDK exposes an API-key option.

Amaad Martin added 5 commits July 28, 2026 23:04
VertexAiSessionService and VertexAiMemoryBankService resolved an Express
Mode API key in their constructors and then built the Agent Engine client
without it, producing a service that authenticated with ADC against
projects/undefined/locations/undefined.

The @google-cloud/vertexai Client constructor only accepts project,
location and apiEndpoint, so the key can never be sent. Throw a shared,
actionable error instead of silently dropping the credential.
…vices

Mock the Agent Engine Client in both suites so the default client path can
be asserted without network or credentials, and make the pre-existing
no-project/location test hermetic by stubbing the express-mode env vars.
Address review feedback: shorten the error text and its doc comment, stop
advertising expressModeApiKey in the fallback message now that it can
never work, and table the three express-mode throw cases.
…sage

Second review pass: GOOGLE_API_KEY is never read when
GOOGLE_GENAI_USE_VERTEXAI is unset, the agentEngineId guard already runs
first structurally, and enumerating the upstream constructor options in
the error would go stale.
Amaad Martin added 3 commits July 31, 2026 15:13
Temporarily takes upstream/main's copy of this file so the merge of
upstream/main lands conflict-free; the express mode tests are restored in
the following commit.
… merge

Re-applies the express mode suite on top of upstream's version of this
file, which gained the ttl/expireTime and ApiError 404 tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant