b3a26a6aff3896d7577be7f42ce94a7d841c91c7
max
  Wed Sep 23 16:41:09 2026 -0700
hgTrackUi: let a faceted composite load its metadata from any curated hub copy, refs #29344

Each sandbox attaches its own copy of a curated hub like hs1
(/gbdb/hs1/hubs/<curatedHubPrefix>/hub.txt), so a link to another
sandbox's copy arrived on other servers with that hub missing from the
cart, and the metadata file was refused. Now any copy in the curated
directory from dbDb is accepted. A hub that is not in the cart is
checked by its URL in hubStatus only, without fetching it.

diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c
index 46c8521ba90..ed832247de7 100644
--- src/hg/hgTrackUi/hgTrackUi.c
+++ src/hg/hgTrackUi/hgTrackUi.c
@@ -4441,44 +4441,45 @@
 for (p = fetchableSettings; *p != NULL; p++)
     {
     char *val = trackDbSetting(tdb, *p);
     if (val != NULL && sameString(val, fileUrl))
         return TRUE;
     }
 return FALSE;
 }
 
 static boolean trackIsFromCuratedHub(char *db, char *track,
                                      struct hubConnectStatus *hubStatusList)
 /* Check if a hub track comes from the curated hub that dbDb names for this assembly.
  * A curated hub such as hs1 keeps its data outside the hub.txt directory, so
  * fileUrlMatchesHub rejects it, but its trackDb is admin-configured and as
  * trustworthy as a native track's.  A user hub attached to the same assembly is
- * not, hence the match against the one hub dbDb names. */
+ * not, hence the match against the curated directory dbDb names.  Any copy in
+ * that directory counts, not only this server's curatedHubPrefix, so that a link
+ * to one sandbox's copy (/gbdb/hs1/hubs/<name>/hub.txt) also works on another. */
 {
-char *curatedUrl = NULL;
-if (!hubConnectGetCuratedUrl(trackHubSkipHubName(db), &curatedUrl) || isEmpty(curatedUrl))
-    return FALSE;
-curatedUrl = hReplaceGbdb(curatedUrl);
 unsigned hubId = hubIdFromTrackName(track);
 struct hubConnectStatus *hubStatus;
 for (hubStatus = hubStatusList; hubStatus != NULL; hubStatus = hubStatus->next)
     {
     if (hubStatus->id == hubId)
-        return sameOk(hubStatus->hubUrl, curatedUrl);
+        return hubConnectIsCuratedHubUrl(trackHubSkipHubName(db), hubStatus->hubUrl);
     }
-return FALSE;
+// Not in the cart: each server attaches its own curated copy for the assembly,
+// so a link to another sandbox's copy arrives here with a different hub in the
+// cart.  Check that hub by id, reading only its URL.
+return hubConnectIdIsCuratedHub(trackHubSkipHubName(db), hubId);
 }
 
 void handleFileFetch(struct cart *cart)
 /* Checks if a requested file is a legal request based on an attached cart or
  * native track.  If so, retrieves the file content via UDC and retransmits
  * it as the page content. */
 {
 char *genome = NULL;
 getDbAndGenome(cart, &database, &genome, NULL);
 initGenbankTableNames(database);
 
 char *fileUrl = cartOptionalString(cart, "fileUrl");
 char *urlClone = cloneString(fileUrl);
 cgiDecode(urlClone, urlClone, strlen(urlClone));
 fileUrl = resolveDotDots(urlClone);