diff --git a/.github/labeler.yml b/.github/labeler.yml index 3fcdadadb6b..f4d16d86e6b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -212,6 +212,7 @@ - redbot/core/_cli.py - redbot/core/_debuginfo.py - redbot/setup.py + - tests/core/test_dry_run.py "Category: Core - Help": - redbot/core/commands/help.py "Category: Core - i18n": diff --git a/redbot/core/bot.py b/redbot/core/bot.py index 359b65e3657..e5f901d9d3e 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -2275,7 +2275,15 @@ async def _delete_helper(m): async def close(self): """Logs out of Discord and closes all connections.""" - await super().close() + try: + await super().close() + except AttributeError as e: + if "'Red' object has no attribute '_AutoShardedClient__queue'" in str(e): + # The client never finished starting up, so the queue was never created. + pass + else: + raise e + await _drivers.get_driver_class().teardown() try: if self.rpc_enabled: diff --git a/tests/core/test_dry_run.py b/tests/core/test_dry_run.py new file mode 100644 index 00000000000..d18ea316971 --- /dev/null +++ b/tests/core/test_dry_run.py @@ -0,0 +1,36 @@ +"""Integration test ensuring `redbot --dry-run` exits cleanly.""" + +from __future__ import annotations + +import subprocess +import sys + + +def test_dry_run_exits_cleanly(): + """Running `redbot --dry-run` should exit with code 0 and without AttributeError.""" + result = subprocess.run( + [ + sys.executable, + "-m", + "redbot", + "--no-instance", + "--dry-run", + "--no-prompt", + "--token", + "dummy_token_for_testing", + "--prefix", + "!", + ], + capture_output=True, + text=True, + timeout=30, + ) + + assert result.returncode == 0, ( + "Expected redbot dry-run to exit with code 0, " + f"got {result.returncode}.\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}" + ) + assert "AttributeError" not in result.stderr, ( + "redbot dry-run emitted AttributeError on stderr, indicating shutdown guard failed.\n" + f"STDERR:\n{result.stderr}" + )