24a1b93cb1cb3590e987bf5c2910fed600d44a66
max
  Tue Sep 15 05:48:40 2026 -0700
hgc: a mistyped detailsScript setting must not take down the details page, refs #38353

The null case was covered but nothing else was.  jsonParse() aborts on malformed JSON and
jsonObjectVal() aborts on any value that is not an object, so a hub with
"detailsScript.histogram.myField 5" in its trackDb lost the whole details page, not just
that one plot.  Caught now: the setting is dropped and the rest of the page is drawn.

diff --git src/hg/hgc/bigBedClick.c src/hg/hgc/bigBedClick.c
index 45dd8e7b564..c403b709d14 100644
--- src/hg/hgc/bigBedClick.c
+++ src/hg/hgc/bigBedClick.c
@@ -7,30 +7,31 @@
 #include "wiggle.h"
 #include "cart.h"
 #include "hgc.h"
 #include "hubConnect.h"
 #include "hCommon.h"
 #include "hgColors.h"
 #include "bigBed.h"
 #include "hui.h"
 #include "subText.h"
 #include "web.h"
 #include "chromAlias.h"
 #include "quickLift.h"
 #include "hgConfig.h"
 #include "jsHelper.h"
 #include "jsonParse.h"
+#include "errCatch.h"
 #include "jsonWrite.h"
 #include "net.h"
 #include "trackHub.h"
 
 static void bigGenePredLinks(char *track, char *item)
 /* output links to genePred driven sequence dumps */
 {
 printf("<H3>Links to sequence:</H3>\n");
 printf("<UL>\n");
 puts("<LI>\n");
 hgcAnchorSomewhere("htcTranslatedPredMRna", item, "translate", seqName);
 printf("Translated Protein</A> from genomic DNA\n");
 puts("</LI>\n");
 
 puts("<LI>\n");
@@ -636,35 +637,44 @@
                 jsonWriteObjectStart(jw, NULL);
                 jsonWriteString(jw, "field", fp->name);
                 // Look up field value from bigBed extra fields
                 char *fv = "";
                 if (extraFieldPairs)
                     {
                     char *found = slPairFindVal(extraFieldPairs, fp->name);
                     if (found)
                         fv = found;
                     }
                 jsonWriteString(jw, "value", fv);
                 // Parse trackDb JSON config and merge its keys into this object
                 char *jsonConfig = fp->val;
                 if (isNotEmpty(jsonConfig))
                     {
-                    struct jsonElement *configEl = jsonParse(jsonConfig);
-                    // jsonObjectVal hands back NULL for a JSON null, and the hash
-                    // routines below dereference their argument, so a hub writing
-                    // "detailsScript.<plotType>.<field> null" would crash us.
-                    struct hash *configHash = jsonObjectVal(configEl, "detailsScript config");
+                    /* This text comes out of a hub's trackDb, so it may be anything at
+                     * all.  jsonParse aborts on malformed JSON and jsonObjectVal aborts on
+                     * a value that is not an object, so one mistyped setting - "histogram
+                     * myField 5" - would otherwise take down the whole details page.
+                     * jsonObjectVal also hands back NULL for a JSON null, and the hash
+                     * routines below dereference their argument.  Catch all of it, drop
+                     * the setting and carry on with the rest of the page. */
+                    struct hash *configHash = NULL;
+                    struct errCatch *errCatch = errCatchNew();
+                    if (errCatchStart(errCatch))
+                        configHash = jsonObjectVal(jsonParse(jsonConfig),
+                                                   "detailsScript config");
+                    errCatchEnd(errCatch);
+                    errCatchFree(&errCatch);
                     if (configHash == NULL)
                         {
                         jsonWriteObjectEnd(jw);
                         continue;
                         }
                     struct hashEl *cel, *celList = hashElListHash(configHash);
                     for (cel = celList; cel != NULL; cel = cel->next)
                         {
                         // A config key ending in "Url" names a file, by the same convention
                         // trackSettingIsFile() uses. The JS does not fetch it directly: it
                         // asks hgTrackUi for it, which checks the path against the hubs on
                         // this cart and reads it with udc. So resolve a relative path here
                         // against the track's own bigDataUrl, which works whether the hub
                         // was loaded over http or from a local path. A path the author
                         // already made absolute is left alone.