14a339480313e5b687eb899766347021ca94b41d
chmalee
  Thu Jul 30 16:12:03 2026 -0700
Call errCatchEnd() before handling the caught error in the tusd hooks, refs #37963

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git src/hg/hgHubConnect/hooks/pre-finish.c src/hg/hgHubConnect/hooks/pre-finish.c
index ecc1d99d844..751c6ecd3f1 100644
--- src/hg/hgHubConnect/hooks/pre-finish.c
+++ src/hg/hgHubConnect/hooks/pre-finish.c
@@ -232,47 +232,49 @@
                 else if (isTwoBit)
                     {
                     // user's hub.txt is authoritative; just flip rows to assemblyHub.
                     upgradeExistingHubToAssembly(row, userDataDir, encodedParentDir);
                     }
                 }
             unlockHubDir(hubLockFd);
             // first make the parentDir rows
             makeParentDirRows(row->userName, sqlDateToUnixTime(row->lastModified), row->db, row->parentDir, userDataDir, row->hubType);
             row->parentDir = encodedParentDir ? hubNameFromPath(encodedParentDir) : "";
             addHubSpaceRowForFile(row);
             fprintf(stderr, "added hubSpace row for file '%s'\n", fileName);
             fflush(stderr);
             }
         }
+    // pop the handlers before handling the error, the cleanup below can itself
+    // errAbort, which would longjmp back into this same block
+    errCatchEnd(errCatch);
     if (errCatch->gotError)
         {
         // App-level reject: exit 0 + RejectUpload=true is the tusd protocol for
         // forwarding HTTPResponse verbatim. Non-zero gets wrapped.
         rejectUpload(response, errCatch->message->string);
         // must remove the tusd temp files so if the users tries again after a temp error
         // the upload will work
         if (tusFile)
             {
             mustRemove(tusFile);
             mustRemove(tusInfo);
             }
         // TODO: if the first mysql request in createNewTempHubForUpload() works but then
         // either of makeParentDirRows() or addHubSpaceRowForFile() fails, we need to also
         // drop any rows we may have added because the upload didn't full go through
         exitStatus = 0;
         }
-    errCatchEnd(errCatch);
     }
 // always print a response no matter what
 jsonPrintToFile(response, NULL, stdout, 0);
 return exitStatus;
 }
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 optionInit(&argc, argv, options);
 if (argc != 1)
     usage();
 return preFinish();
 }