Skip to content

fix(cli): stop slash completion render loop#3533

Merged
LaZzyMan merged 1 commit into
QwenLM:mainfrom
LaZzyMan:fix/slash-completion-update-depth
Apr 23, 2026
Merged

fix(cli): stop slash completion render loop#3533
LaZzyMan merged 1 commit into
QwenLM:mainfrom
LaZzyMan:fix/slash-completion-update-depth

Conversation

@LaZzyMan

Copy link
Copy Markdown
Collaborator

TLDR

Stabilize the useResumeCommand() callback dependencies so slash-command argument completion no longer re-runs on every render. This fixes the interactive CLI crash where typing /model could get stuck on "Loading suggestions..." and repeatedly throw Maximum update depth exceeded.

Screenshots / Video Demo

N/A — no permanent UI change. This PR fixes an interactive re-render loop in slash-command completion.

Dive Deeper

User-visible behavior

In the interactive CLI, typing a slash command that enters argument completion mode could trigger an infinite render loop. A concrete reproduction was:

  1. Type /model
  2. Type a trailing space
  3. Observe the suggestions area stay on Loading suggestions...
  4. The app repeatedly logs Maximum update depth exceeded

This was easiest to hit when the command's completion() returned null or an empty array, because the completion flow kept re-running while also writing completion state back into React.

Root cause

The loop was not caused by modelCommand itself. The actual instability came from useResumeCommand().

useResumeCommand() memoized handleResume() with the entire historyManager object in its dependency list. useHistory() returns a fresh object each render, even though its methods are individually stable. That meant:

  • handleResume() got recreated every render
  • slashCommandActions in AppContainer changed every render because it depends on handleResume
  • commandContext in slashCommandProcessor changed every render because it depends on actions
  • useSlashCompletion() treats commandContext as an effect dependency, so argument completion re-fired every render
  • the completion effect kept setting loading/suggestion state, producing a render-feedback loop

So /model merely exposed the bug because it enters slash argument completion and its completion() path was invoked over and over.

Fix

This PR removes the unstable object dependency from useResumeCommand():

  • derive hasHistoryManager as a boolean guard
  • destructure clearItems and loadHistory from historyManager
  • depend on those stable callbacks instead of the whole historyManager object
  • use optional calls when resetting and loading history

This keeps handleResume() referentially stable across normal renders, which in turn keeps slashCommandActions, commandContext, and slash completion stable.

Reviewer Test Plan

  1. Start the interactive CLI.
  2. Type /model and then a space.
  3. Confirm the app does not enter a render loop.
  4. Confirm the UI does not spam Maximum update depth exceeded.
  5. Confirm the suggestions area exits loading normally when no suggestions are returned.
  6. Sanity check /resume still works and restores prior session history.

Good extra cases:

  • /model --
  • /model --fast
  • any other slash command with argument completion that can return no results

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

Related to the interactive slash-command completion render loop triggered by /model when command argument completion returned no suggestions.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No issues found. LGTM! ✅ — gpt-5.4 via Qwen Code /review

@LaZzyMan LaZzyMan merged commit 78037d9 into QwenLM:main Apr 23, 2026
13 checks passed
TaimoorSiddiquiOfficial pushed a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request Apr 23, 2026
Port upstream QwenLM/qwen-code improvements to HopCode:

fix(core): scope StreamingToolCallParser per stream (QwenLM#3516)
- Add ConverterStreamContext interface with per-stream StreamingToolCallParser
- Add createStreamContext() factory on OpenAIContentConverter
- convertOpenAIChunkToGemini() now takes ctx as explicit arg
- Drop shared streamingToolCallParser instance field and resetStreamingToolCalls()
- ContentGenerationPipeline creates one context per stream entry
- Update all tests to use createStreamContext() API
- Fixes concurrent subagent streams corrupting each other (NO_RESPONSE_TEXT)

feat(cli): combine elapsed + timeout in shell time indicator (QwenLM#3512)
- formatters: add FormatDurationOptions with hideTrailingZeros option
- ToolElapsedTime: accept optional timeoutMs; render (elapsed · timeout N)
- ToolMessage: extract timeoutMs from AnsiOutputDisplay, feed to ToolElapsedTime
- CompactToolGroupDisplay: add getShellTimeoutMs() helper, thread timeoutMs
- AnsiOutput: drop timeoutMs from ShellStatsBarProps (now inline in elapsed)

fix(cli): stabilize resume callback deps (QwenLM#3533)
- Destructure historyManager into clearItems/loadHistory stable refs
- Use hasHistoryManager boolean guard in useCallback dep array

chore: bump version 0.14.41 -> 0.15.1 across all packages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TaimoorSiddiquiOfficial pushed a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request Apr 23, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
chiga0 pushed a commit that referenced this pull request Apr 24, 2026
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants