fix: race condition on shared account info causes KeyError during download#4769
Open
waldh4ri wants to merge 1 commit into
Open
fix: race condition on shared account info causes KeyError during download#4769waldh4ri wants to merge 1 commit into
waldh4ri wants to merge 1 commit into
Conversation
…nload BaseAccount.choose() cleared self.info before resyncing it for the newly selected user. The account plugin instance is shared across all download threads for a given hoster, and downloader plugins read self.account.info["login"]["password"] without holding the account lock. When one thread cleared info while another was mid-download, the reader could see an empty dict and crash. Replace the clear with sync(), which rebuilds info for the current user directly, so readers never observe an empty dict.
waldh4ri
force-pushed
the
fix/account-info-race-condition
branch
from
July 18, 2026 16:00
cdedff7 to
3eb06a3
Compare
GammaC0de
reviewed
Jul 18, 2026
|
|
||
| else: | ||
| self.user = user | ||
| self.info.clear() |
Member
There was a problem hiding this comment.
i think calling clear() and then sync() would be better, please check and report
Author
There was a problem hiding this comment.
That would reintroduce the race (with a shorter window) and could lead to the same KeyError. self.info update mechanism must be atomic because we don't acquire self.account.lock before reading info in any of hoster.py/AlldebridCom.py/etc. sync update to info is atomic and never leave it in a blank state:
d = {"login": {}, "data": {}}
for k, v in u.items():
...
self.info.update(d)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
BaseAccount.choose()clearsself.infobefore resyncing it for the newly selected user:The account plugin instance is shared across every download thread for a given hoster (
AccountManager.get_account_plugincaches a single instance per plugin). Downloader plugins readself.account.info["login"]["password"]directly, without holding the account's lock.When one thread runs
choose()and clearsinfowhile another thread is mid-download reading from it, the reader can observe an empty dict and crash:This affects every downloader that reads
account.info["login"][...]directly (AlldebridCom, PremiumizeMe, RealDebrid, TorboxApp, FikperCom, ZeveraCom, and others), and shows up intermittently under concurrent downloads, causing some files in a batch to fail while others succeed.A prior change (add some more locks) added
@locktologin(),logout(), andsync(), but the readers in the downloader plugins never acquire that lock, so the empty-dict window was never actually closed.Fix
Replace
self.info.clear()withself.sync().sync()already runs a few lines later via theself.loggedcheck, so the explicit clear is redundant. Callingsync()directly rebuildsinfo["login"]/info["data"]for the new user in place, soself.infotransitions straight from the old user's data to the new user's data and is never left empty for a reader on another thread to observe.