Impact
Maintenance scripts and jobs that act on behalf of a system username (such as Redirect fixer, and Global rename script) are not working or not working correctly because User::newFromName returns a named anonymous user instead of a system user.
$ mwscript fixDoubleRedirects.php --wiki labswiki MWException: User::addToDatabase: hit a key conflict attempting to insert user 'Unknown user' row, […] from line 2798 of /srv/mediawiki/php-1.40.0-wmf.17/includes/user/User.php
This currently affects 1844 accounts over 952 different wikis. Full dump at P42930,
Example
$ mwscript sql.php --wiki labswiki > SELECT * FROM user WHERE user_name='Redirect fixer'; [user_id] => 31136 [user_name] => Redirect fixer [user_editcount] => 24 […] $ mwscript eval.php --wiki labswiki > $user = User::newFromName( 'Redirect fixer' ); > $user->getName(); "Redirect fixer" > $user->isItemLoaded('id'); true # wrong > $user->getId(); 0 # wrong > $user->isRegistered(); false # wrong > $user->addToDatabase(); MWException from line 2798 of /srv/mediawiki/php-1.40.0-wmf.17/includes/user/User.php: User::addToDatabase: hit a key conflict attempting to insert user 'Unknown user' row, but it was not present in select! #4 /srv/mediawiki/php-1.40.0-wmf.17/includes/jobqueue/jobs/DoubleRedirectJob.php(270): User->addToDatabase() #5 /srv/mediawiki/php-1.40.0-wmf.17/includes/jobqueue/jobs/DoubleRedirectJob.php(175): DoubleRedirectJob->getUser() #6 /srv/mediawiki/php-1.40.0-wmf.17/maintenance/fixDoubleRedirects.php(120): DoubleRedirectJob->run()
Note how the error appears to be for the insertion of a different username: "Unknown user".
Cause
There appear two distinct causes.
- User::newFromName / User::load fails to handle actor rows with a registered local name (i.e. not IP or foreign/imported name) but a NULL actor_user value instead of the local user ID.
This bug was introduced with change 525605 in 2019 when User::load handling for the loadFrom "name" case was switched from querying the user table to querying the actor table instead.
As a result, it is falling back to no-oping the load and treating it as an unregistered/anonymous user object. This results in addToDatabase() producing a fatal error as the row already exists. And during the re-try, User::loadFromDatabase similarly fails because it simultenously exists in the DB yet consistently has no user ID to find in the actor table. This is where the username gets swapped to "Unknown user".
- Apparently it was (or still is?) possible for the actor system to create rows for locally existent users without storing the user ID in actor_user.
This currently affects 1844 accounts over 952 different wikis. Full dump at P42930.