e986a0d4e9eac49158dc152a7d6d5ac96823b825
chmalee
  Tue Sep 30 14:05:11 2025 -0700
Another spot to remove 0 len chars, this time in the autocomplete lib, refs #36387

diff --git src/hg/js/autocompleteCat.js src/hg/js/autocompleteCat.js
index 4e17af0e219..7fd88f25a63 100644
--- src/hg/js/autocompleteCat.js
+++ src/hg/js/autocompleteCat.js
@@ -75,45 +75,46 @@
         // options.onServerReply (if given) is a function (Array, term) -> Array that
         // post-processes the list of items returned by the server before the list is
         // passed back to autocomplete for rendering.
         // The following two options apply only when using our locally modified jquery-ui:
         // If options.enterSelectsIdentical is true, then if the user hits Enter in the text input
         // and their term has an exact match in the autocomplete results, that result is selected.
         // options.onEnterTerm (if provided) is a callback function (jqEvent, jqUi) invoked
         // when the user hits Enter, after handling enterSelectsIdentical.
 
         // The function closure allows us to keep a private cache of past searches.
         var cache = {};
 
         var doSearch = function(term, acCallback) {
             // Look up term in searchObj and by sending an ajax request
             var timestamp = new Date().getTime();
-            var url = options.baseUrl + encodeURIComponent(term);
+            let cleanedTerm = term.replace(/[\u200b-\u200d\u2060\uFEFF]/g,''); // remove 0 len chars
+            var url = options.baseUrl + encodeURIComponent(cleanedTerm);
             if (!options.baseUrl.startsWith("hubApi")) {
                 // hubApi doesn't tolerate extra arguments
                 url += '&_=' + timestamp;
             }
             // put up a loading icon so users know something is happening
             toggleSpinner(true, options);
             $.getJSON(url)
                .done(function(results) {
                 if (_.isFunction(options.onServerReply)) {
-                    results = options.onServerReply(results, term);
+                    results = options.onServerReply(results, cleanedTerm);
                 }
                 // remove the loading icon
                 toggleSpinner(false, options);
-                cache[term] = results;
+                cache[cleanedTerm] = results;
                 acCallback(results);
             });
             // ignore errors to avoid spamming people on flaky network connections
             // with tons of error messages (#8816).
         };
 
         var autoCompleteSource = function(request, acCallback) {
             // This is a callback for jqueryui.autocomplete: when the user types
             // a character, this is called with the input value as request.term and an acCallback
             // for this to return the result to autocomplete.
             // See http://api.jqueryui.com/autocomplete/#option-source
             if (this.element[0].id === "positionInput" && request.term.length < 2) {
                 let searchStack = window.localStorage.getItem("searchStack");
                 if (request.term.length === 0 && searchStack) {
                     let searchObj = JSON.parse(searchStack);