30a4424ce1b185ef0e6e95969e0b688d686dfb8d
chmalee
  Wed May 6 11:52:40 2026 -0700
HubSpace assembly hub fixes: fixes 1,5,6,8 and prettify error messages, refs #37411

diff --git src/hg/hgHubConnect/hooks/pre-create.c src/hg/hgHubConnect/hooks/pre-create.c
index 5c777d2dfe0..7cc7cc11b51 100644
--- src/hg/hgHubConnect/hooks/pre-create.c
+++ src/hg/hgHubConnect/hooks/pre-create.c
@@ -6,30 +6,32 @@
  * client. */
 #include "common.h"
 #include "linefile.h"
 #include "hash.h"
 #include "options.h"
 #include "wikiLink.h"
 #include "customTrack.h"
 #include "userdata.h"
 #include "jsonQuery.h"
 #include "jsHelper.h"
 #include "errCatch.h"
 #include "obscure.h"
 #include "hooklib.h"
 #include "hubSpaceKeys.h"
 #include "htmshell.h"
+#include "hdb.h"
+#include "genark.h"
 
 void usage()
 /* Explain usage and exit. */
 {
 errAbort(
   "pre-create - tus daemon pre-create hook program\n"
   "usage:\n"
   "   pre-create < input\n"
   );
 }
 
 /* Command line validation table. */
 static struct optionSpec options[] = {
    {NULL, 0},
 };
@@ -113,57 +115,75 @@
         if (newQuota > maxQuota)
             {
             errAbort("File '%s' is too large, need %s free space but current used space is %s out of %s", reqFileName, prettyFileSize(reqFileSize), prettyFileSize(currQuota), prettyFileSize(maxQuota));
             }
         char *reqFileType = jsonQueryString(req, "", "Event.Upload.MetaData.fileType", NULL);
         if (!isFileTypeRecognized(reqFileType))
             {
             errAbort("File type '%s' for file '%s' is not accepted at this time", reqFileType, reqFileName);
             }
         char *reqGenome = jsonQueryString(req, "", "Event.Upload.MetaData.genome", NULL);
         if (!reqGenome)
             {
             errAbort("Genome selection is NULL for file '%s' is invalid. Please choose the correct genome", reqFileName);
             }
 
+        // Block 2bit uploads whose genome name collides with a UCSC native database or GenArk hub.
+        if (sameOk(reqFileType, "2bit") && reqGenome[0] &&
+            (hDbExists(reqGenome) || isGenArk(reqGenome)))
+            {
+            char *hubName = reqParentDir ? hubNameFromPath(reqParentDir) : "";
+            char *existingHubType = existingHubTypeForDir(userName, hubName);
+            if (!sameOk(existingHubType, "assemblyHub"))
+                errAbort("Genome name '%s' collides with a UCSC native assembly or GenArk hub. "
+                         "hgTracks would load the UCSC assembly instead of your 2bit. "
+                         "Open the file card and pick a different name for your "
+                         "assembly (e.g. '%s_hub') in the Genome field.",
+                         reqGenome, reqGenome);
+            }
+
         // we've passed all the checks so we can return that we are good to upload the file
         if (exitStatus == 0)
             {
             // set the location of the upload to the location it will ultimately live
             char *location = setUploadPath(userName, reqFileName, reqParentDir, forceOverwrite);
             if (!location)
                 {
                 errAbort("Error setting upload path in pre-create for file '%s'. This is an"
                         " issue with our server, please email genome-www@soe.ucsc.edu with your"
                         " userName so we can investigate.", reqFileName);
                 }
             struct hash *changeObjHash = hashNew(0);
             struct hash *pathObjHash = hashNew(0);
             struct jsonElement *changeObj = newJsonObject(changeObjHash);
             struct jsonElement *pathObj = newJsonObject(pathObjHash);
 
             jsonObjectAdd(pathObj, "Path", newJsonString(location));
             jsonObjectAdd(changeObj, "Storage", pathObj);
             jsonObjectAdd(changeObj, "ID", newJsonString(makeRandomKey(128)));
             jsonObjectAdd(response, "ChangeFileInfo", changeObj);
             fillOutHttpResponseSuccess(response);
             }
         }
     if (errCatch->gotError)
         {
+        // App-level reject: tusd treats exit 0 + RejectUpload=true as a clean
+        // rejection and forwards our HTTPResponse body verbatim. Non-zero
+        // would be wrapped in "ERR_INTERNAL_SERVER_ERROR ... from hook
+        // endpoint: ..." which buries the real message.
         rejectUpload(response, errCatch->message->string);
-        exitStatus = 1;
+        exitStatus = 0;
         }
     errCatchEnd(errCatch);
     }
 // always print a response no matter what
 jsonPrintToFile(response, NULL, stdout, 0);
 return 0;
 }
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 optionInit(&argc, argv, options);
 if (argc != 1)
     usage();
 return preCreate();