Skip to content

fix: infinite /partial request loop triggered by search input - #3863

Merged
jonpspri merged 7 commits into
mainfrom
3861-infinitely-loop-on-search
Mar 28, 2026
Merged

fix: infinite /partial request loop triggered by search input#3863
jonpspri merged 7 commits into
mainfrom
3861-infinitely-loop-on-search

Conversation

@marekdano

@marekdano marekdano commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

🐛 Bug-fix PR

Closes #3861

📌 Summary

Fix infinite loop of /partial endpoint requests triggered when typing in admin UI search bars (servers, tools, resources, gateways, prompts, agents).
Remove unnecessary search input re-initialization from htmx:afterSwap and htmx:afterSettle handlers, since search inputs live outside the swapped table DOM and persist across partial refreshes.

Fix npm high vulnerabilities

🔁 Reproduction Steps

  1. Go to http://localhost:8080/
  2. Select one the pages e.g. MCP servers
  3. Open the browser console and search for server
  4. Observe that calling /partial endpoint is called infinitely until the search is cleared

Root Cause

initializeSearchInputs() attached the input event listener before setting .value from URL state, so the value assignment immediately fired the listener, queuing a /partial reload whose swap re-initialized inputs — creating an infinite cycle

💡 Fix Description

Changes

  1. Reorder value assignment and listener attachment in initializeSearchInputs() — set .value before adding the input listener so initialization doesn't trigger a reload
  2. Remove search input re-initialization from htmx:afterSwap — search inputs are not part of the swapped table content; only update filter status
  3. Remove search input re-initialization from htmx:afterSettle — same reasoning; only update filter status
  4. Remove eager reload on init — the visible-panel check was queueing an immediate loadSearchablePanel call, but the server already returns correctly filtered data in the initial page load
  5. Add regression tests (tests/js/admin-search-init-loop.test.js, 7 tests) covering:
    - Value restoration from URL state does not fire the input listener
    - No htmx.ajax call is made during initialization
    - User typing still triggers a debounced /partial reload (positive test)
    - htmx:afterSwap / htmx:afterSettle on table targets do not re-trigger search initialization
    - Search input value and listener survive table content swaps
  6. run npm audit fix to fix high vulnerabilities

🧪 Verification

Check Command Status
Lint suite make lint
Unit tests make test
Coverage ≥ 80 % make coverage
Manual regression no longer fails steps / screenshots

📐 MCP Compliance (if relevant)

  • Matches current MCP spec
  • No breaking change to MCP clients

✅ Checklist

  • Code formatted (make black isort pre-commit)
  • No secrets/credentials committed

@marekdano marekdano added bug Something isn't working ui User Interface labels Mar 25, 2026
@marekdano marekdano added this to the Release 1.0.0 milestone Mar 25, 2026
@marekdano
marekdano force-pushed the 3861-infinitely-loop-on-search branch from baed620 to d91bb5a Compare March 25, 2026 18:25
Comment thread package.json Outdated
gcgoncalves
gcgoncalves previously approved these changes Mar 26, 2026

@gcgoncalves gcgoncalves 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.

Tested and working fine! :)

@marekdano marekdano added release-fix Critical bugfix required for the release MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe labels Mar 26, 2026
vishu-bh
vishu-bh previously approved these changes Mar 26, 2026

@vishu-bh vishu-bh 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.

LGTM 🚀

The fix looks correct, the regression coverage is targeted

@marekdano
marekdano dismissed stale reviews from gcgoncalves and vishu-bh via 6ea2322 March 27, 2026 10:08
@marekdano
marekdano force-pushed the 3861-infinitely-loop-on-search branch 2 times, most recently from a22f190 to 154c0ba Compare March 27, 2026 12:26
gcgoncalves
gcgoncalves previously approved these changes Mar 27, 2026

@gcgoncalves gcgoncalves 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.

Tested and works fine. The code is correct and the proper tests were added.

@gcgoncalves gcgoncalves 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.

Have you considered using the createMemoizedInit function? I created it for a similar init loop, and the goal back then was to apply it to all inits, to prevent exactly this loop issue.

@marekdano

Copy link
Copy Markdown
Collaborator Author

@gcgoncalves
The code is already using createMemoizedInit correctly.

It already implements the createMemoizedInit pattern at line 19009 in admin.js:

const {
    init: initializeSearchInputsMemoized,
    debouncedInit: initializeSearchInputsDebounced,
    reset: resetSearchInputsState,
} = createMemoizedInit(initializeSearchInputs, 300, "SearchInputs");

The fix works by:

  • Keeping the memoized initialization (already there)
  • Removing the problematic resetSearchInputsState() + initializeSearchInputsDebounced() calls from HTMX event handlers
  • Only calling updateFilterStatus() on swaps instead of re-initializing

The key change in the diff shows:

BEFORE (caused infinite loop):

document.body.addEventListener("htmx:afterSwap", function (event) {
    resetSearchInputsState();  // ❌ This bypassed memoization guards
    initializeSearchInputsDebounced();  // ❌ Triggered reload → swap → repeat
});

AFTER (fixed):

document.body.addEventListener("htmx:afterSwap", function (event) {
    updateFilterStatus();  // ✅ Only updates UI, no re-init
});

No changes needed - the implementation already follows the suggested pattern.

Marek Dano added 4 commits March 28, 2026 17:19
Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Marek Dano and others added 3 commits March 28, 2026 17:19
…lic registry

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Update stale doc comment on initializeSearchInputs() to reflect that
HTMX handlers no longer re-invoke it. Prefix unused entityType parameter
with underscore to satisfy linter. Add URL cleanup in beforeEach to
prevent test contamination on mid-test failures.

Signed-off-by: Jonathan Springer <jps@s390x.com>
@jonpspri
jonpspri force-pushed the 3861-infinitely-loop-on-search branch from f94402c to 6c8263d Compare March 28, 2026 19:37
@jonpspri
jonpspri merged commit 92b106b into main Mar 28, 2026
38 checks passed
@jonpspri
jonpspri deleted the 3861-infinitely-loop-on-search branch March 28, 2026 20:02
msureshkumar88 pushed a commit that referenced this pull request May 13, 2026
* fix: infinite /partial request loop triggered by search input

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: merging conflicts

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: npm deps for high vulnerabilities

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: package-lock.json file

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: E401 authentication error on npm install and pointing to the public registry

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: package-lock.json file

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: improve search init comments, lint, and test robustness

Update stale doc comment on initializeSearchInputs() to reflect that
HTMX handlers no longer re-invoke it. Prefix unused entityType parameter
with underscore to satisfy linter. Add URL cleanup in beforeEach to
prevent test contamination on mid-test failures.

Signed-off-by: Jonathan Springer <jps@s390x.com>

---------

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Marek Dano <Marek.Dano@ibm.com>
Co-authored-by: Jonathan Springer <jps@s390x.com>
kamath-a pushed a commit to kamath-a/mcp-context-forge that referenced this pull request Jun 26, 2026
* fix: infinite /partial request loop triggered by search input

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: merging conflicts

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: npm deps for high vulnerabilities

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: package-lock.json file

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: E401 authentication error on npm install and pointing to the public registry

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: package-lock.json file

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>

* fix: improve search init comments, lint, and test robustness

Update stale doc comment on initializeSearchInputs() to reflect that
HTMX handlers no longer re-invoke it. Prefix unused entityType parameter
with underscore to satisfy linter. Add URL cleanup in beforeEach to
prevent test contamination on mid-test failures.

Signed-off-by: Jonathan Springer <jps@s390x.com>

---------

Signed-off-by: Marek Dano <Marek.Dano@ibm.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Co-authored-by: Marek Dano <Marek.Dano@ibm.com>
Co-authored-by: Jonathan Springer <jps@s390x.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe release-fix Critical bugfix required for the release ui User Interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][UI]: infinite /partial request loop triggered by search input

5 participants