From 08fdb7ea873d81cc4ddb024e04f75eca55324462 Mon Sep 17 00:00:00 2001 From: splitsec2 <35583321+splitsec2@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:22:21 -0600 Subject: [PATCH] fix(startup): the reconcile guard catches the same errors the rest of main.py does (#1540) The start-up reconcile runs at import, before the coordinator starts, inside a try that named five exception types and left out `TypeError`. `main.py` already defines `_IMPORT_OPERATIONAL_ERRORS` for this shape and the other module-level guards use it, so this switches to it. An unexpected `TypeError` from the reconcile would otherwise fail the import of `shelfmark.main`, and the worker would fail to boot on every restart. I didn't use `except Exception` because BLE001 is on and nothing in main.py does. The test reloads main with the reconcile raising `TypeError`, and with `OSError` as the control (that one already passes on main). Full non-e2e suite passes, ruff and basedpyright are clean. Found in a Claude pass over 1.5.0. --- shelfmark/main.py | 2 +- tests/core/test_startup_reconcile_guard.py | 39 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/core/test_startup_reconcile_guard.py diff --git a/shelfmark/main.py b/shelfmark/main.py index f8076627..6d3e3ea4 100644 --- a/shelfmark/main.py +++ b/shelfmark/main.py @@ -285,7 +285,7 @@ if user_db is not None and download_history_service is not None: _reconciled["marked"], _reconciled["reopened"], ) - except (ImportError, OSError, RuntimeError, ValueError, sqlite3.Error) as exc: + except _IMPORT_OPERATIONAL_ERRORS as exc: logger.warning("Could not reconcile interrupted downloads at startup: %s", exc) # Start download coordinator diff --git a/tests/core/test_startup_reconcile_guard.py b/tests/core/test_startup_reconcile_guard.py new file mode 100644 index 00000000..96efa680 --- /dev/null +++ b/tests/core/test_startup_reconcile_guard.py @@ -0,0 +1,39 @@ +"""A reconcile that cannot run must never stop the app from starting.""" + +from __future__ import annotations + +import importlib +from unittest.mock import patch + +import pytest + +from shelfmark.core.logger import CustomLogger + + +@pytest.mark.parametrize("error", [TypeError("unexpected row shape"), OSError("disk went away")]) +def test_a_failing_startup_reconcile_only_logs_a_warning(error): + """The module-level guard in main.py runs before the coordinator starts. + + An error it does not catch fails the import of shelfmark.main, and the worker then + fails to boot on every restart. + """ + # Patch the class, not main.logger: reloading main builds a new logger object. + with ( + patch("shelfmark.download.orchestrator.start"), + patch("shelfmark.download.warmup.start"), + patch( + "shelfmark.core.startup_reconcile.reconcile_interrupted_downloads", + side_effect=error, + ) as reconcile, + patch.object(CustomLogger, "warning") as warning, + ): + import shelfmark.main as main + + importlib.reload(main) + + # Importing and then reloading runs the module body twice, so only check it was reached. + assert reconcile.called + assert any( + "Could not reconcile interrupted downloads at startup" in str(call.args[0]) + for call in warning.call_args_list + )