gemini-cli - 💡(How to fix) Fix ACP/non-interactive auth can rewrite durable `security.auth.selectedType` from OAuth to API-key auth when `GEMINI_API_KEY` is ambient [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
google-gemini/gemini-cli#25687Fetched 2026-04-20 12:15:24
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
labeled ×1

We observed a surprising auth-state mutation in Gemini CLI during an ACP / non-interactive flow.

In an isolated HOME with durable Gemini auth pinned to Google sign-in (oauth-personal), a risky ACP path with ambient GEMINI_API_KEY was able to drift the saved auth selection away from OAuth and into the API-key lane.

The concerning part is not only the runtime fallback. The ACP/non-interactive flow appears able to persist the fallback auth method back into durable local settings for later runs.

I am reporting this as observed behavior that may be unintended. If this is actually expected for ACP/non-interactive flows, then I think there should at least be a way to opt out of durable auth mutation.

Root Cause

We observed a surprising auth-state mutation in Gemini CLI during an ACP / non-interactive flow.

In an isolated HOME with durable Gemini auth pinned to Google sign-in (oauth-personal), a risky ACP path with ambient GEMINI_API_KEY was able to drift the saved auth selection away from OAuth and into the API-key lane.

The concerning part is not only the runtime fallback. The ACP/non-interactive flow appears able to persist the fallback auth method back into durable local settings for later runs.

I am reporting this as observed behavior that may be unintended. If this is actually expected for ACP/non-interactive flows, then I think there should at least be a way to opt out of durable auth mutation.

Code Example

async authenticate(req: acp.AuthenticateRequest): Promise<void> {
  const { methodId } = req;
  const method = z.nativeEnum(AuthType).parse(methodId);
  ...
  await this.context.config.refreshAuth(method, apiKey, baseUrl, headers);
  this.settings.setValue(
    SettingScope.User,
    'security.auth.selectedType',
    method,
  );
}

---

const effectiveAuthType = configuredAuthType || getAuthTypeFromEnv();
RAW_BUFFERClick to expand / collapse

Summary

We observed a surprising auth-state mutation in Gemini CLI during an ACP / non-interactive flow.

In an isolated HOME with durable Gemini auth pinned to Google sign-in (oauth-personal), a risky ACP path with ambient GEMINI_API_KEY was able to drift the saved auth selection away from OAuth and into the API-key lane.

The concerning part is not only the runtime fallback. The ACP/non-interactive flow appears able to persist the fallback auth method back into durable local settings for later runs.

I am reporting this as observed behavior that may be unintended. If this is actually expected for ACP/non-interactive flows, then I think there should at least be a way to opt out of durable auth mutation.

Version / environment

  • Observed on Gemini CLI v0.38.2
  • v0.38.2 is also the current latest GitHub release at the time of filing
  • Surface: ACP / non-interactive execution
  • Prior durable auth state in the isolated HOME:
    • ~/.gemini/settings.json
      • security.auth.selectedType = oauth-personal
      • security.auth.enforcedType = oauth-personal
    • valid cached Google account / OAuth credential files present

What we observed

We ran two isolated-HOME stage cases without touching the live ~/.gemini directory.

Case A, hardened path

  • durable Gemini settings pinned to oauth-personal
  • an outer launcher kept explicit selection pressure toward the OAuth lane
  • GEMINI_API_KEY was still intentionally injected into the staged process

Result:

  • the staged call did not complete successfully, but the durable auth state remained pinned to oauth-personal
  • no auth drift happened

Case B, risky path

  • removed the outer selection guard
  • launched Gemini through a bare ACP path with ambient GEMINI_API_KEY

Result:

  • after the staged run, the isolated ~/.gemini/settings.json had drifted to selectedType = gemini-api-key
  • the staged OAuth/account lane was no longer the active durable lane for subsequent runs

This makes the problem look like credential precedence plus durable state mutation, not just an expired cache.

Minimal reproduction shape

I do not yet have a pure-gemini-cli-only repro without an ACP client, but the minimal observed shape was:

  1. Create an isolated HOME
  2. Stage ~/.gemini/settings.json with:
    • selectedType = oauth-personal
    • enforcedType = oauth-personal
  3. Stage valid cached OAuth/account files in that isolated HOME
  4. Export GEMINI_API_KEY into the environment
  5. Start Gemini CLI through an ACP/non-interactive path that does not independently pin the OAuth lane
  6. Execute one non-interactive prompt turn
  7. Inspect the isolated ~/.gemini/settings.json

Observed result in the risky path:

  • security.auth.selectedType changed from oauth-personal to gemini-api-key

The exact helper I used on my side was an external ACP client. If helpful, I can provide a smaller reproduction packet for that path.

Why this seems surprising

From the docs, security.auth.selectedType reads as the durable selected auth method, and security.auth.enforcedType reads as the required auth method:

  • docs/reference/configuration.md

I did not find an obvious note in the README/configuration docs saying that ACP/non-interactive fallback auth is expected to rewrite the durable auth selection based on ambient env credentials.

Source seams that look relevant

I realize bundled code can differ from source, so below are the corresponding source-level seams from main that appear relevant.

1. ACP authenticate persists the chosen method into durable settings

packages/cli/src/acp/acpClient.ts

async authenticate(req: acp.AuthenticateRequest): Promise<void> {
  const { methodId } = req;
  const method = z.nativeEnum(AuthType).parse(methodId);
  ...
  await this.context.config.refreshAuth(method, apiKey, baseUrl, headers);
  this.settings.setValue(
    SettingScope.User,
    'security.auth.selectedType',
    method,
  );
}

2. Non-interactive auth validation allows env discovery into the effective auth type

packages/cli/src/validateNonInterActiveAuth.ts

const effectiveAuthType = configuredAuthType || getAuthTypeFromEnv();

Those two together look risky in ACP/non-interactive flows:

  1. env makes the API-key lane eligible
  2. ACP authenticates with that method
  3. durable selectedType gets rewritten

Expected behavior

One of these would feel safer:

  1. ACP/non-interactive fallback auth should not rewrite durable security.auth.selectedType, or
  2. only explicit user-driven auth-selection flows should persist selectedType, or
  3. ACP/non-interactive flows should require an explicit opt-in before changing durable auth preference, or
  4. if this behavior is intentional, there should be a documented auth-lock / do-not-persist mechanism for mixed OAuth + API-key environments

Questions

  • Is this durable auth rewrite in ACP/non-interactive flows intentional?
  • If yes, would you accept a feature request for an auth-lock / no-persist mode?
  • If no, does the acpClient.ts persistence path look like the right fix seam?

Thanks. I can provide more exact before/after state and the ACP-side repro details if that would help narrow this further.

extent analysis

TL;DR

To prevent unintended durable auth-state mutation in Gemini CLI during ACP/non-interactive flows, consider modifying the acpClient.ts to not persist the chosen auth method into durable settings when the flow is non-interactive.

Guidance

  • Review the acpClient.ts file to understand how the authenticate function persists the chosen auth method into durable settings.
  • Consider adding a check to prevent persistence when the flow is non-interactive, to avoid overwriting the user's selected auth type.
  • Evaluate the validateNonInterActiveAuth.ts file to ensure that env discovery into the effective auth type does not inadvertently lead to durable auth-state mutation.
  • Test the changes with both interactive and non-interactive flows to verify that the durable auth state is not mutated unexpectedly.

Example

// Modified authenticate function in acpClient.ts
async authenticate(req: acp.AuthenticateRequest): Promise<void> {
  const { methodId } = req;
  const method = z.nativeEnum(AuthType).parse(methodId);
  ...
  if (req.isInteractive) { // Add a check for interactive flows
    await this.context.config.refreshAuth(method, apiKey, baseUrl, headers);
    this.settings.setValue(
      SettingScope.User,
      'security.auth.selectedType',
      method,
    );
  }
}

Notes

The provided code snippets and analysis suggest that the issue lies in the interaction between the acpClient.ts and validateNonInterActiveAuth.ts files. However, without the full codebase and context, it is difficult to provide a definitive solution. The suggested modification to acpClient.ts is a potential starting point for addressing the issue.

Recommendation

Apply a workaround by modifying the acpClient.ts file to prevent persistence of the chosen auth method into durable settings during non-interactive flows, as this seems to be the most direct way to address the issue without introducing new features or complex logic.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of these would feel safer:

  1. ACP/non-interactive fallback auth should not rewrite durable security.auth.selectedType, or
  2. only explicit user-driven auth-selection flows should persist selectedType, or
  3. ACP/non-interactive flows should require an explicit opt-in before changing durable auth preference, or
  4. if this behavior is intentional, there should be a documented auth-lock / do-not-persist mechanism for mixed OAuth + API-key environments

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

gemini-cli - 💡(How to fix) Fix ACP/non-interactive auth can rewrite durable `security.auth.selectedType` from OAuth to API-key auth when `GEMINI_API_KEY` is ambient [1 participants]