gemini-cli - 💡(How to fix) Fix BeforeModel hook with llm_request modification destroys non-text parts, causing tool-call loops [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#25558Fetched 2026-04-17 08:55:11
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
labeled ×2

Root Cause

Root cause: toHookLLMRequest intentionally filters to text-only for a simplified hook API (by design — documented in the source). fromHookLLMRequest does not reverse that mapping — it replaces all contents wholesale instead of merging text changes back into the original structure.

Code Example

> /about
About Gemini CLI

CLI Version      0.38.1
Git Commit       7f5580034
Model            Auto (Gemini 3)
Sandbox          no sandbox
OS               darwin
Auth Method      Signed in with Google (email redacted)
Tier             Gemini Code Assist in Google One AI Pro
RAW_BUFFERClick to expand / collapse

What happened?

HookTranslatorGenAIv1.fromHookLLMRequest rebuilds the SDK request's contents as text-only from hookRequest.messages, discarding every non-text part (functionCall, functionResponse, inlineData, thought, etc.) from baseRequest.

Source location (v0.38.1 bundled install): bundle/chunk-ZTFHMKKJ.js:313774 — originally from packages/core/src/hooks/hookTranslator.ts.

This breaks any BeforeModel hook that modifies text in conversations containing tool calls. The symptom is an infinite tool-call loop: the hook strips tool call/response history, the model loses its prior tool context, and it re-invokes the same tool.

Root cause: toHookLLMRequest intentionally filters to text-only for a simplified hook API (by design — documented in the source). fromHookLLMRequest does not reverse that mapping — it replaces all contents wholesale instead of merging text changes back into the original structure.

Reproduction (confirmed against v0.38.1 on macOS):

  1. Register a BeforeModel hook that modifies text in llm_request.messages (e.g., PII redaction)
  2. Start a conversation that triggers at least one tool call
  3. In a follow-up turn, submit a prompt the hook modifies
  4. Observe the model repeatedly re-invoking the same tool — it never sees prior tool results

Queries where the hook returns {} (no modification) work correctly because the original SDK request is preserved unchanged.

What did you expect to happen?

A BeforeModel hook modifying text should update text-bearing contents in place while preserving all non-text parts (functionCall, functionResponse, etc.) and their positional ordering in the conversation history.

Client information

<details> <summary>Client Information</summary>

Platform: macOS (Darwin 25.4.0), Node v24.14.0

> /about
About Gemini CLI

CLI Version      0.38.1
Git Commit       7f5580034
Model            Auto (Gemini 3)
Sandbox          no sandbox
OS               darwin
Auth Method      Signed in with Google (email redacted)
Tier             Gemini Code Assist in Google One AI Pro
</details>

Anything else we need to know?

A fix with passing tests was submitted as #23340 (auto-closed on 2026-04-05 under the 14-day no-help-wanted policy, not for code reasons — Gemini Code Assist's own review noted "The implementation is robust... I found no issues"). Happy to rebase and resubmit against the current bundled layout if this issue gets labeled help wanted.

Real-world impact: this bug blocks any hook-based PII blinding/redaction use case (FERPA, HIPAA, etc.) because those hooks must modify text, and production agent loops always contain tool calls.

extent analysis

TL;DR

The issue can be fixed by modifying the fromHookLLMRequest function to merge text changes back into the original structure instead of replacing all contents wholesale.

Guidance

  • Review the fromHookLLMRequest function in hookTranslator.ts to understand how it rebuilds the SDK request's contents and identify the part that discards non-text parts.
  • Modify the fromHookLLMRequest function to preserve non-text parts (functionCall, functionResponse, etc.) and their positional ordering in the conversation history.
  • Test the modified function with the provided reproduction steps to ensure it resolves the infinite tool-call loop issue.
  • Consider rebasing and resubmitting the fix from #23340 against the current bundled layout if this issue gets labeled help wanted.

Example

No code snippet is provided as the issue does not include the exact implementation of fromHookLLMRequest. However, the fix should involve modifying this function to merge text changes back into the original structure.

Notes

The issue is specific to the fromHookLLMRequest function and its handling of non-text parts. The fix should focus on modifying this function to preserve the original structure of the conversation history.

Recommendation

Apply the workaround by modifying the fromHookLLMRequest function to merge text changes back into the original structure, as this is the root cause of the issue and a fix has already been submitted and reviewed.

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…

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 BeforeModel hook with llm_request modification destroys non-text parts, causing tool-call loops [1 participants]