From 481dbf2e9b9cf8248c795e5c1dbc66df58a3a43a Mon Sep 17 00:00:00 2001 From: "Paul J. Dorn" Date: Sun, 25 Jan 2026 09:41:39 +0100 Subject: [PATCH] Publish full exception when the application fails to load (#3462) * Python3: refactor returned traceback Exceptions provide __traceback__ reference since Python 3.0 (and creating cyclic references has not been big deal since Python 2.2) * --reload: publish entire exception, not just traceback This is dangerous insofar as the exception text is more likely to contain secrets than the quoted lines from traceback are. However, the difference between the two is minor compared to the primary danger of enabling this on a production machine, so focus on that instead! --- docs/content/reference/settings.md | 5 +++++ gunicorn/config.py | 4 ++++ gunicorn/workers/base.py | 16 +++++----------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/content/reference/settings.md b/docs/content/reference/settings.md index 76b58d5b..ea9333af 100644 --- a/docs/content/reference/settings.md +++ b/docs/content/reference/settings.md @@ -73,6 +73,11 @@ because it consumes less system resources. In order to use the inotify reloader, you must have the ``inotify`` package installed. +!!! warning + Enabling this will change what happens on failure to load the + the application: While the reloader is active, any and all clients + that can make requests can see the full exception and traceback! + ### `reload_engine` **Command line:** `--reload-engine STRING` diff --git a/gunicorn/config.py b/gunicorn/config.py index ffbc1b27..1e0565f2 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -952,6 +952,10 @@ class Reload(Setting): .. note:: In order to use the inotify reloader, you must have the ``inotify`` package installed. + .. warning:: + Enabling this will change what happens on failure to load the + the application: While the reloader is active, any and all clients + that can make requests can see the full exception and traceback! ''' diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py index c7a46e0e..ca6cf10b 100644 --- a/gunicorn/workers/base.py +++ b/gunicorn/workers/base.py @@ -128,6 +128,7 @@ class Worker: time.sleep(0.1) sys.exit(0) + self.log.warning("Reloader is on. Use in development only!") reloader_cls = reloader_engines[self.cfg.reload_engine] self.reloader = reloader_cls(extra_files=self.cfg.reload_extra_files, callback=changed) @@ -151,19 +152,12 @@ class Worker: self.log.exception(e) - # fix from PR #1228 - # storing the traceback into exc_tb will create a circular reference. - # per https://docs.python.org/2/library/sys.html#sys.exc_info warning, - # delete the traceback after use. - try: - _, exc_val, exc_tb = sys.exc_info() - self.reloader.add_extra_file(exc_val.filename) + if self.reloader is not None and e.filename is not None: + self.reloader.add_extra_file(e.filename) - tb_string = io.StringIO() - traceback.print_tb(exc_tb, file=tb_string) + with io.StringIO() as tb_string: + traceback.print_exception(e, file=tb_string) self.wsgi = util.make_fail_app(tb_string.getvalue()) - finally: - del exc_tb def init_signals(self): # reset signaling