From 3e9e953c7590a8c362fdd260f3881783f06832db Mon Sep 17 00:00:00 2001 From: Ethan Stoner Date: Fri, 28 Aug 2026 08:53:28 -0700 Subject: [PATCH] keep one account's failure from ending a batched run Only SessionNotCreatedException was isolated. Every other way an account can fail reached main() and took the accounts after it with them: a driver that will not start for another reason, an unwritable profile directory, the first page not loading, the browser dying mid-run, or quit() raising because it was already gone. With REWARDS_ACCOUNTS=one,two,three and the middle one failing, three never ran and main() exited on a traceback instead of an exit code. Catch it around run_account, report it the way a failed task is reported, and carry on. KeyboardInterrupt is left alone so Ctrl-C still stops the run. The quit() in run_account is guarded too, so a tidy-up that raises no longer hides the failure it was tidying up after. --- README.md | 2 +- src/main.py | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b95ab9e..bb8b340 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Each is signed in once by hand, the same way as the single profile, using its ow msedge --user-data-dir="\data-dir\personal" --profile-directory=Default https://rewards.bing.com ``` -They run one after another, and a profile that fails to start is reported and skipped rather than ending the run. Leave `REWARDS_ACCOUNTS` unset and everything behaves exactly as before, using the single profile in `data-dir`. +They run one after another, and an account that fails is reported and skipped rather than ending the run, whether it fails to start or dies partway through. Leave `REWARDS_ACCOUNTS` unset and everything behaves exactly as before, using the single profile in `data-dir`. # Docker diff --git a/src/main.py b/src/main.py index 8ce6b7a..1b9e362 100644 --- a/src/main.py +++ b/src/main.py @@ -55,7 +55,16 @@ def run_account(account: accounts.Account) -> bool: rewards = rewards_tasks.RewardsTaskUtils(driver) rewards.complete_all_tasks() finally: - driver.quit() + try: + driver.quit() + except Exception as exc: + # quit() raises when the browser is already gone. Letting it out + # here would replace whatever actually went wrong with the tidy-up's + # own error, and the process it is meant to end is dead anyway. + logger.warning( + "%s: the driver did not shut down cleanly: %s", + account.name, log_utils.exception_summary(exc) + ) return True @@ -76,8 +85,21 @@ def main() -> int: if len(configured) > 1: logger.info("=== account: %s ===", account.name) - if run_account(account): - started += 1 + # One account must not be able to end the batch. complete_all_tasks + # already contains a task that fails, and run_account names the profile + # that is already open, but everything else - a driver that will not + # start for some other reason, the browser dying mid-run, a page that + # never loads - reached here and took the remaining accounts with it. + # KeyboardInterrupt is deliberately not caught: Ctrl-C means stop. + try: + if run_account(account): + started += 1 + except Exception as exc: + logger.error( + "[FAIL] %s: %s: %s", + account.name, type(exc).__name__, log_utils.exception_summary(exc), + exc_info=logger.isEnabledFor(logging.DEBUG) + ) if len(configured) > 1: logger.info("%s/%s accounts ran", started, len(configured))