a71215bcc4a26e1641770d82127dc57ced1842c8
braney
  Tue Mar 10 12:37:04 2026 -0700
Detect tracks that list themselves as their own parent, preventing infinite loop refs #35986

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

diff --git src/hg/lib/trackDbCustom.c src/hg/lib/trackDbCustom.c
index 89704f273bd..17e20d596a7 100644
--- src/hg/lib/trackDbCustom.c
+++ src/hg/lib/trackDbCustom.c
@@ -646,30 +646,35 @@
 /* adjust settings on supertrack members after verifying they have
  * a supertrack configured in this trackDb */
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
     {
     if (tdbIsSuperTrack(tdb) || tdb->parent != NULL)
         continue;
     setting = trackDbLocalSetting(tdb, "parent");
     if (!setting)
         setting = trackDbLocalSetting(tdb, "superTrack");  // Old style
     if (!setting)
         continue;
     wordCt = chopLine(cloneString(setting), words);
     assert(differentString("on", words[0])); // already weeded out "superTrack on"
     char *parentName = maybeSkipHubPrefix(words[0]);
     tdb->parent = hashFindVal(superHash, parentName);
+    if (tdb->parent == tdb)
+        {
+        warn("Track %s lists itself as its own parent, ignoring.", tdb->track);
+        tdb->parent = NULL;
+        }
     if (tdb->parent)
         {
         tdbMarkAsSuperTrackChild(tdb);
         tdb->parentName = cloneString(parentName);
         if (wordCt > 1)
             tdb->visibility = max(0, hTvFromStringNoAbort(words[1]));
         }
     freeMem(words[0]);
     }
 hashFree(&superHash);
 }
 
 char *trackDbOrigAssembly(struct trackDb *tdb)
 /* return setting from trackDb, if any */
 {
@@ -1030,30 +1035,32 @@
     if (tdbIsSuperTrack(tdb))
         tdb->next = NULL;
     else
         slAddHead(&superlessList, tdb);
     }
 
 /* Do subtrack hierarchy - filling in parent and subtracks fields. */
 for (tdb = superlessList; tdb != NULL; tdb = next)
     {
     next = tdb->next;
     char *subtrackSetting = trackDbLocalSetting(tdb, "parent");
     if (subtrackSetting != NULL
     && !tdbIsSuperTrackChild(tdb)) // superChildren cannot be in both subtracks list AND tdbList
         {
 	char *parentName = cloneFirstWord(subtrackSetting);
+	if (sameString(parentName, tdb->track))
+	    errAbort("Track %s lists itself as its own parent", tdb->track);
 	struct trackDb *parent = hashFindVal(trackHash, parentName);
 	if (parent != NULL)
         {
         if (trackDbLocalSetting(tdb, "container"))
             {
             errAbort("Composite track '%s' cannot have child track '%s',"
                 " which is a container  multiWig.", parentName, tdb->track);
             }
         slAddHead(&parent->subtracks, tdb); // composite/multiWig children are ONLY subtracks
         tdb->parent = parent;
         }
 	else
 	    {
 	    errAbort("Parent track %s of child %s doesn't exist", parentName, tdb->track);
 	    }