Skip to content

fix(cli): preserve approved shell commands across confirmation retries - #29201

Closed
chelsealong wants to merge 1 commit into
google-gemini:mainfrom
chelsealong:fix-29197-shell-confirmation-loop
Closed

chelsealong wants to merge 1 commit into
google-gemini:mainfrom
chelsealong:fix-29197-shell-confirmation-loop

Conversation

@chelsealong

Copy link
Copy Markdown

Fixes #29197

What happened

When a TOML custom command contains multiple !{...} shell injections that each need confirmation, the CLI could get stuck asking for permission forever — cycling between commands, and never converging even if the user chose "always allow" for every prompt.

Root cause

FileCommandLoader's command action re-runs the entire prompt-processing pipeline from scratch on every retry. When ShellProcessor throws ConfirmationRequiredError, slashCommandProcessor.ts's confirm_shell_commands handler recurses into handleSlashCommand with a fresh one-time allowlist containing only the commands approved in that round:

return await handleSlashCommand(
  result.originalInvocation.raw,
  // Pass the approved commands as a one-time grant for this execution.
  new Set(approvedCommands),
  undefined,
  false,
);

If a second, distinct command still needs confirmation on the retry (e.g. discovered in a later part of the prompt), a second round begins. That round's one-time allowlist only contains the newly approved command — the one approved in the first round is dropped, because:

  • it isn't in this round's approvedCommands, and
  • the persisted sessionShellAllowlist React state update from the first round (setSessionShellAllowlist) may not have been reflected into the commandContext this in-flight recursive call closure is using yet, even when the user picked "Always".

So the command originally approved gets asked again, and this can cycle indefinitely.

Fix

Accumulate approvals across the whole confirmation chain instead of replacing them each round:

new Set([
  ...(oneTimeShellAllowlist ?? []),
  ...approvedCommands,
]),

Now every retry keeps every command approved so far in that chain, regardless of whether the backing React state has re-rendered yet, so the loop always converges once every distinct command has been confirmed at least once.

Testing

Added a regression test in packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx ("Shell command confirmation > accumulates approvals across multiple confirmation rounds instead of looping forever") that simulates a command action which re-checks context.session.sessionShellAllowlist from scratch on each invocation and asks for cmd1 then cmd2 in turn.

  • Confirmed the test fails against the pre-fix code: it hangs and times out after 60s because the third round asks for cmd1 again (reproducing the infinite loop).
  • Confirmed the test passes with the fix: both confirmations resolve in two rounds.
$ npx vitest run src/ui/hooks/slashCommandProcessor.test.tsx
 ✓ src/ui/hooks/slashCommandProcessor.test.tsx (39 tests)
   ✓ useSlashCommandProcessor > Shell command confirmation > accumulates approvals across multiple confirmation rounds instead of looping forever

 Test Files  1 passed (1)
      Tests  39 passed (39)

Also ran:

  • npx eslint src/ui/hooks/slashCommandProcessor.ts src/ui/hooks/slashCommandProcessor.test.tsx — clean
  • npx tsc --noEmit (packages/cli) — clean
  • npx vitest run src/services/prompt-processors/shellProcessor.test.ts — 34 tests passed (unaffected)

AI assistance disclosure

This PR was prepared with the assistance of an AI coding agent (Claude), including root-cause analysis, the fix, and the regression test. All changes were verified by running the project's real lint/typecheck/test commands as shown above.

When a TOML custom command needs confirmation for multiple shell
commands, each retry re-runs the command action from scratch and only
carries forward the commands approved in that single round via a
one-time allowlist. Commands approved in an earlier round of the same
retry chain were dropped instead of persisted, since the freshly
memoized `commandContext.session.sessionShellAllowlist` used for the
next round may not yet reflect a prior `setSessionShellAllowlist`
update. This caused the confirmation prompts to cycle between commands
forever, matching the "toml command interpolation stuck in infinite
loop when multiple commands need permission" report, including the
"not even allowing the command for the rest of the session breaks the
loop" symptom.

Fix by accumulating the newly approved commands with whatever was
already granted earlier in the same confirmation chain when recursing,
so every retry keeps every command approved so far regardless of
render timing.

Fixes google-gemini#29197
@chelsealong
chelsealong requested a review from a team as a code owner September 4, 2026 05:46
@github-actions github-actions Bot added the size/m A medium sized PR label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 116
  • Additions: +113
  • Deletions: -3
  • Files changed: 2

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where the CLI would enter an infinite loop when executing custom commands that require multiple shell command confirmations. By modifying the handleSlashCommand recursion to preserve previously approved commands in the one-time allowlist, the system now correctly converges even when the underlying session state has not yet updated.

Highlights

  • Fix infinite confirmation loop: Updated the shell command confirmation logic to accumulate approved commands across multiple rounds instead of replacing them, preventing the CLI from repeatedly asking for previously approved commands.
  • Regression testing: Added a new test case in slashCommandProcessor.test.tsx that simulates a multi-step command requiring multiple confirmations to ensure the fix correctly handles accumulated approvals.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the slash command processor to accumulate approved shell commands across multiple confirmation rounds. Specifically, in slashCommandProcessor.ts, the handleSlashCommand retry call now merges any previously granted commands in oneTimeShellAllowlist with the newly approved commands, preventing infinite loops when a command action re-runs and re-checks permissions before the session allowlist has updated. Additionally, a comprehensive unit test has been added to slashCommandProcessor.test.tsx to verify this multi-step confirmation behavior. There are no review comments, so I have no feedback to provide.

@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/core Issues related to User Interface, OS Support, Core Functionality priority/p1 Important and should be addressed in the near term. area/security Issues related to security labels Sep 4, 2026
@gemini-cli

gemini-cli Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Hi there! Thank you for your interest in contributing to Gemini CLI.

To ensure we maintain high code quality and focus on our prioritized roadmap, we only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.

This PR will be closed in 7 days if it remains without that designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

@gemini-cli

gemini-cli Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

This pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality area/security Issues related to security priority/p1 Important and should be addressed in the near term. priority/p2 Important but can be addressed in a future release. size/m A medium sized PR status/pr-nudge-sent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

toml command interpolation stuck in infinite loop when multiple commands need permission

1 participant