8c172e6c4b985af79bf1a1f47c9db92ce0578999
max
  Tue Sep 15 05:47:36 2026 -0700
errCatch: put errAbort's doContentType back the way it found it, refs #38353

hVaUserAbort() turns doContentType on so that a user error reaches the browser when the CGI
has not pushed a warn handler of its own.  Inside an errCatch that is pointless -- the abort
is caught and never reaches the default handler -- and the flag stayed on afterwards, so a
later genuine abort on a page that had already written its header would print a second
Content-Type line into the body.  Everywhere else in the tree the flag is something a caller
turns back off.

errCatchPushHandlers() now records the flag and errCatchEnd() restores it, which covers every
caller rather than just the one that set it.  errAbortGetDoContentType() is the reader.

diff --git src/lib/errCatch.c src/lib/errCatch.c
index 6caf8ea5b70..c343ba5f2b8 100644
--- src/lib/errCatch.c
+++ src/lib/errCatch.c
@@ -75,40 +75,46 @@
 longjmp(errCatchStack->jmpBuf, -1);
 }
 
 static void errCatchWarnHandler(char *format, va_list args)
 /* Write an error to top of errCatchStack. */
 {
 struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack;
 errCatchStack->gotWarning = TRUE;
 dyStringVaPrintf(errCatchStack->message, format, args);
 dyStringAppendC(errCatchStack->message, '\n');
 }
 
 boolean errCatchPushHandlers(struct errCatch *errCatch)
 /* Push error handlers.  Not usually called directly. */
 {
+/* An abort caught here never reaches the default handler, so anything the aborting code set
+ * up for that handler - hVaUserAbort turning doContentType on so a user error reaches the
+ * browser - is not wanted and, worse, would still be set for the next abort, which may be a
+ * real one on a page that has already written its header. */
+errCatch->savedDoContentType = errAbortGetDoContentType();
 pushAbortHandler(errCatchAbortHandler);
 pushWarnHandler(errCatchWarnHandler);
 struct errCatch **pErrCatchStack = getStack();
 slAddHead(pErrCatchStack, errCatch);
 return TRUE;
 }
 
 void errCatchEnd(struct errCatch *errCatch)
 /* Restore error handlers and pop self off of catching stack. */
 {
+errAbortSetDoContentType(errCatch->savedDoContentType);
 popWarnHandler();
 popAbortHandler();
 struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack;
 if (errCatch != errCatchStack)
    errAbort("Mismatch between errCatch and errCatchStack");
 *pErrCatchStack = errCatch->next;
 }
 
 void errCatchReWarn(struct errCatch *errCatch)
 /* Re-warn any warnings that happened even though no abort happened 
  * to make them visible. */
 {
 if (errCatch->gotWarning && !errCatch->gotError)
     {
     warn("%s", errCatch->message->string);