gemini-cli - 💡(How to fix) Fix [Bug] Race condition in HookSystemMessage rendering causes UI layout inconsistency [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#25861Fetched 2026-04-24 06:13:53
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
labeled ×1

There is a race condition in how HookSystemMessage events (emitted by hooks like StopHook) are rendered in the CLI. If a hook emits a message while the assistant still has pending response content (streaming or buffering), the message is immediately injected into the chat history. This causes the hook's feedback to appear out of order, often interleaved with or preceding the final response block.

Root Cause

In interactiveCli, the handleHookSystemMessage handler calls historyManager.addItem immediately. It does not wait for the assistant's current turn to finish (i.e., it doesn't check pendingHistoryItems).

Code Example

const queuedHookSystemMessagesRef = useRef([]);
const flushQueuedHookSystemMessages = useCallback(() => {
  if (queuedHookSystemMessagesRef.current.length === 0) return;
  const queuedMessages = queuedHookSystemMessagesRef.current;
  queuedHookSystemMessagesRef.current = [];
  for (const payload of queuedMessages) {
    historyManager.addItem(
      {
        type: "info",
        text: payload.message,
        source: payload.hookName
      },
      Date.now()
    );
  }
}, [historyManager]);

useEffect(() => {
  if (pendingHistoryItems.length === 0) {
    flushQueuedHookSystemMessages();
  }
}, [pendingHistoryItems, flushQueuedHookSystemMessages]);

const handleHookSystemMessage = (payload) => {
  if (pendingHistoryItems.length > 0) {
    queuedHookSystemMessagesRef.current.push(payload);
    return;
  }
  historyManager.addItem(
    {
      type: "info",
      text: payload.message,
      source: payload.hookName
    },
    Date.now()
  );
};
RAW_BUFFERClick to expand / collapse

Description

There is a race condition in how HookSystemMessage events (emitted by hooks like StopHook) are rendered in the CLI. If a hook emits a message while the assistant still has pending response content (streaming or buffering), the message is immediately injected into the chat history. This causes the hook's feedback to appear out of order, often interleaved with or preceding the final response block.

Reproduction

  1. Use a hook (e.g., StopHook) that emits a HookSystemMessage at the end of a tool call or response.
  2. Observe that the message sometimes appears in the middle of the assistant's response if the response hasn't been fully committed to history yet.

Root Cause

In interactiveCli, the handleHookSystemMessage handler calls historyManager.addItem immediately. It does not wait for the assistant's current turn to finish (i.e., it doesn't check pendingHistoryItems).

Proposed Fix

Implement a queuing mechanism to defer hook system messages until the assistant's response is complete.

  1. Create a queuedHookSystemMessagesRef to store messages.
  2. In handleHookSystemMessage, check if pendingHistoryItems.length > 0. If so, push to the queue.
  3. Use a useEffect monitoring pendingHistoryItems to flush the queue when it becomes empty.

Reference Implementation:

const queuedHookSystemMessagesRef = useRef([]);
const flushQueuedHookSystemMessages = useCallback(() => {
  if (queuedHookSystemMessagesRef.current.length === 0) return;
  const queuedMessages = queuedHookSystemMessagesRef.current;
  queuedHookSystemMessagesRef.current = [];
  for (const payload of queuedMessages) {
    historyManager.addItem(
      {
        type: "info",
        text: payload.message,
        source: payload.hookName
      },
      Date.now()
    );
  }
}, [historyManager]);

useEffect(() => {
  if (pendingHistoryItems.length === 0) {
    flushQueuedHookSystemMessages();
  }
}, [pendingHistoryItems, flushQueuedHookSystemMessages]);

const handleHookSystemMessage = (payload) => {
  if (pendingHistoryItems.length > 0) {
    queuedHookSystemMessagesRef.current.push(payload);
    return;
  }
  historyManager.addItem(
    {
      type: "info",
      text: payload.message,
      source: payload.hookName
    },
    Date.now()
  );
};

extent analysis

TL;DR

Implement a queuing mechanism to defer hook system messages until the assistant's response is complete to resolve the race condition issue.

Guidance

  • Identify the handleHookSystemMessage handler in interactiveCli and modify it to check for pending history items before adding the message to the history.
  • Create a queue to store hook system messages when the assistant's response is not yet complete.
  • Use a mechanism like useEffect to monitor pendingHistoryItems and flush the queue when it becomes empty.
  • Verify the fix by testing the hook with a pending response and checking if the message appears in the correct order.

Example

The provided reference implementation demonstrates how to create a queue and flush it when the pending history items are cleared:

const handleHookSystemMessage = (payload) => {
  if (pendingHistoryItems.length > 0) {
    queuedHookSystemMessagesRef.current.push(payload);
    return;
  }
  historyManager.addItem(
    {
      type: "info",
      text: payload.message,
      source: payload.hookName
    },
    Date.now()
  );
};

Notes

This solution assumes that the pendingHistoryItems array is accurately updated when the assistant's response is complete. If this is not the case, additional modifications may be necessary.

Recommendation

Apply the proposed fix by implementing the queuing mechanism, as it directly addresses the identified root cause of the issue.

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 [Bug] Race condition in HookSystemMessage rendering causes UI layout inconsistency [1 participants]