e389c29f0255935bc714c66ce7182c4a24427adc
braney
  Fri Feb 6 12:13:44 2026 -0800
fixes hubApi trackDb cache bug #37064

diff --git src/hg/hubApi/apiUtils.c src/hg/hubApi/apiUtils.c
index b9cc476def1..c2cdef3dbd0 100644
--- src/hg/hubApi/apiUtils.c
+++ src/hg/hubApi/apiUtils.c
@@ -262,31 +262,42 @@
     i++;
     }
 *nameReturn = namesReturn;
 *typeReturn = typesReturn;
 *jsonTypes = jsonReturn;
 return columnCount;
 }
 
 struct trackHub *errCatchTrackHubOpen(char *hubUrl)
 /* use errCatch around a trackHub open in case it fails */
 {
 struct trackHub *hub = NULL;
 struct errCatch *errCatch = errCatchNew();
 if (errCatchStart(errCatch))
     {
-    hub = trackHubOpen(hubUrl, "");
+    unsigned getHubId(char *url, char **errorMessage);
+    char *errMessage;
+    unsigned hubId = hubFindOrAddUrlInStatusTable(NULL, hubUrl, &errMessage);
+    
+    // if we got an error, throw error
+    if (errMessage != NULL)
+        errAbort("%s", errMessage);
+
+    // use hubId in hubName
+    char buffer[4096];
+    safef(buffer, sizeof buffer, "hub_%d", hubId);
+    hub = trackHubOpen(hubUrl, buffer);
     }
 errCatchEnd(errCatch);
 if (errCatch->gotError)
     {
     apiErrAbort(err404, err404Msg, "error opening hubUrl: '%s', '%s'", hubUrl,  errCatch->message->string);
     }
 errCatchFree(&errCatch);
 return hub;
 }
 
 static int trackDbTrackCmp(const void *va, const void *vb)
 /* Compare to sort based on 'track' name; use shortLabel as secondary sort key.
  * Note: parallel code to hgTracks.c:tgCmpPriority */
 {
 const struct trackDb *a = *((struct trackDb **)va);
@@ -313,35 +324,35 @@
 return tdb;
 }
 
 struct trackDb *findTrackDb(char *track, struct trackDb *tdb)
 /* search tdb structure for specific track, recursion on subtracks */
 {
 struct trackDb *trackFound = NULL;
 
 for (trackFound = tdb; trackFound; trackFound = trackFound->next)
     {
     if (trackFound->subtracks)
 	{
         struct trackDb *subTrack = findTrackDb(track, trackFound->subtracks);
 	if (subTrack)
 	    {
-	    if (sameOk(subTrack->track, track))
+	    if (sameOk(trackHubSkipHubName(subTrack->track), track))
 		trackFound = subTrack;
 	    }
 	}
-    if (sameOk(trackFound->track, track))
+    if (sameOk(trackHubSkipHubName(trackFound->track), track))
 	break;
     }
 
 return trackFound;
 }
 
 boolean allowedBigBedType(char *type)
 /* return TRUE if the big* bed-like type is to be supported
  * add to this list as the big* bed-like supported types are expanded
  */
 {
 if (startsWithWord("bigBarChart", type) ||
     startsWithWord("bigBed", type) ||
     startsWithWord("bigGenePred", type) ||
     startsWithWord("bigInteract", type) ||
@@ -383,31 +394,31 @@
 {
 const struct chromInfo *a = *((struct chromInfo **)va);
 const struct chromInfo *b = *((struct chromInfo **)vb);
 int dif;
 dif = (long) a->size - (long) b->size;
 return dif;
 }
 
 struct trackHubGenome *findHubGenome(struct trackHub *hub, char *genome,
     char *endpoint, char *hubUrl)
 /* given open 'hub', find the specified 'genome' called from 'endpoint' */
 {
 struct trackHubGenome *hubGenome = NULL;
 for (hubGenome = hub->genomeList; hubGenome; hubGenome = hubGenome->next)
     {
-    if (sameString(genome, hubGenome->name))
+    if (sameString(genome, trackHubSkipHubName(hubGenome->name)))
 	break;
     }
 if (NULL == hubGenome)
     apiErrAbort(err400, err400Msg, "failed to find specified genome=%s for endpoint '%s'  given hubUrl '%s'", genome, endpoint, hubUrl);
 return hubGenome;
 }
 
 char *verifyLegalArgs(char *validArgList[])
 /* validArgList is an array of strings for valid arguments
  * returning string of any other arguments not on that list found in
  * cgiVarList(), NULL when none found.
  */
 {
 struct hash *validHash = NULL;
 char *words[32];
@@ -651,30 +662,40 @@
 if (tdb)
     {
     if (tdbIsContainer(tdb) || tdbIsComposite(tdb)
 	|| tdbIsCompositeView(tdb) || tdbIsSuper(tdb))
 	return FALSE;
     else
 	return TRUE;
     }
 else
     return TRUE;	/* might be true */
 }
 
 boolean protectedTrack(char *db, struct trackDb *tdb, char *tableName)
 /* determine if track is off-limits protected data */
 {
+// if the table is from hub, assume db should be from hub as well
+if (isHubTrack(tableName))
+    {
+    char buffer[4096];
+
+    safef(buffer, sizeof buffer, "hub_%d_%s",hubIdFromTrackName(tableName), db);
+
+    db = cloneString(buffer);
+    }
+
 return cartTrackDbIsAccessDenied(db, tableName) || cartTrackDbIsNoGenome(db, tableName);
 }
 
 boolean isWiggleDataTable(char *type)
 /* is this a wiggle data track table */
 {
 if (startsWith("wig", type))
     {
     if (startsWith("wigMaf", type))
 	return FALSE;
     else
 	return TRUE;
     }
 else
      return FALSE;