Page MenuHomePhabricator

FileBackendGroup constructor warning accessing 'directory' in ForeignApiRepo setup arrays
Open, LowPublic

Description

While locally testing with a near-copy of the InstantCommons config for enwiki added for local testing of T404580 I found that running update.php produces a big visible PHP warning in FileBackendGroup where it accesses `$info['directory'] unconditionally. According to the docs on https://www.mediawiki.org/wiki/Manual:$wgForeignFileRepos this key is not required for a ForeignAPIRepo setup. Config and output below.

Expected behavior:

  • works smoothly, no warnings or errors

Actual behavior:

  • PHP warning fires during update.php, dumping a backtrace to console

Took a quick peek at the code, it's just copying stuff between arrays unconditionally. It may be safe to just stick a ?? '' on the end, but should double-check what it's feeding into.

Open questions:

  • does this just always fire a warning and it's going to my logs elsewhere or is there something specific about the updater that's throwing it?

Config in LocalSettings.php:

$wgForeignFileRepos[] = [
	'class' => ForeignAPIRepo::class,
	'name' => 'enwiki',
	'apibase' => 'https://en.wikipedia.org/w/api.php',
	'url' => 'https://upload.wikimedia.org/wikipedia/en',
	'thumbUrl' => 'https://upload.wikimedia.org/wikipedia/en/thumb',
	'hashLevels' => 2,
	'transformVia404' => true,
	'fetchDescription' => true,
	'descriptionCacheExpiry' => 43200,
	'apiThumbCacheExpiry' => 0,
];
...site_stats is populated...done.
...Update 'cleanup empty categories' already logged as completed. Use --force to run it again.
...Update 'MediaWiki\Maintenance\FixAutoblockLogTitles' already logged as completed. Use --force to run it again.
PHP Warning:  Undefined array key "directory" in /opt/homebrew/var/www/w/includes/filebackend/FileBackendGroup.php on line 133
PHP Stack trace:
PHP   1. {main}() /opt/homebrew/var/www/w/maintenance/run:0
PHP   2. require() /opt/homebrew/var/www/w/maintenance/run:3
PHP   3. MediaWiki\Maintenance\MaintenanceRunner->run() /opt/homebrew/var/www/w/maintenance/run.php:53
PHP   4. UpdateMediaWiki->execute() /opt/homebrew/var/www/w/maintenance/includes/MaintenanceRunner.php:696
PHP   5. MediaWiki\Installer\DatabaseUpdater->setFileAccess() /opt/homebrew/var/www/w/maintenance/update.php:216
PHP   6. MediaWiki\FileRepo\RepoGroup->getLocalRepo() /opt/homebrew/var/www/w/includes/installer/DatabaseUpdater.php:1221
PHP   7. MediaWiki\FileRepo\RepoGroup->getRepo($index = 'local') /opt/homebrew/var/www/w/includes/filerepo/RepoGroup.php:350
PHP   8. MediaWiki\FileRepo\RepoGroup->initialiseRepos() /opt/homebrew/var/www/w/includes/filerepo/RepoGroup.php:316
PHP   9. MediaWiki\FileRepo\RepoGroup->newRepo($info = ['class' => 'MediaWiki\\FileRepo\\LocalRepo', 'name' => 'local', 'directory' => '/opt/homebrew/var/www/w/images', 'scriptDirUrl' => '/w', 'favicon' => '/favicon.ico', 'url' => '/w/images', 'hashLevels' => 2, 'thumbScriptUrl' => FALSE, 'transformVia404' => FALSE, 'deletedDir' => '/opt/homebrew/var/www/w/images/deleted', 'deletedHashLevels' => 3, 'updateCompatibleMetadata' => FALSE, 'reserializeMetadata' => FALSE, 'backend' => 'local-backend']) /opt/homebrew/var/www/w/includes/filerepo/RepoGroup.php:394
PHP  10. MediaWiki\FileRepo\LocalRepo->__construct($info = ['class' => 'MediaWiki\\FileRepo\\LocalRepo', 'name' => 'local', 'directory' => '/opt/homebrew/var/www/w/images', 'scriptDirUrl' => '/w', 'favicon' => '/favicon.ico', 'url' => '/w/images', 'hashLevels' => 2, 'thumbScriptUrl' => FALSE, 'transformVia404' => FALSE, 'deletedDir' => '/opt/homebrew/var/www/w/images/deleted', 'deletedHashLevels' => 3, 'updateCompatibleMetadata' => FALSE, 'reserializeMetadata' => FALSE, 'backend' => 'local-backend', 'wanCache' => class Wikimedia\ObjectCache\WANObjectCache { protected $cache = class Wikimedia\ObjectCache\MemcachedPhpBagOStuff { ... }; protected $processCaches = [...]; protected $logger = class MediaWiki\Logger\LegacyLogger { ... }; protected $stats = class Wikimedia\Stats\StatsFactory { ... }; protected $asyncHandler = NULL; protected $broadcastRoute = NULL; protected $useInterimHoldOffCaching = TRUE; protected $epoch = 0; protected $coalesceScheme = 2; private $tracer = class Wikimedia\Telemetry\NoopTracer { ... }; private $missLog = [...]; private $callbackDepth = 0; private $warmupCache = [...]; private $warmupKeyMisses = 0; private $wallClockOverride = NULL }]) /opt/homebrew/var/www/w/includes/filerepo/RepoGroup.php:422
PHP  11. MediaWiki\FileRepo\FileRepo->__construct($info = ['class' => 'MediaWiki\\FileRepo\\LocalRepo', 'name' => 'local', 'directory' => '/opt/homebrew/var/www/w/images', 'scriptDirUrl' => '/w', 'favicon' => '/favicon.ico', 'url' => '/w/images', 'hashLevels' => 2, 'thumbScriptUrl' => FALSE, 'transformVia404' => FALSE, 'deletedDir' => '/opt/homebrew/var/www/w/images/deleted', 'deletedHashLevels' => 3, 'updateCompatibleMetadata' => FALSE, 'reserializeMetadata' => FALSE, 'backend' => 'local-backend', 'wanCache' => class Wikimedia\ObjectCache\WANObjectCache { protected $cache = class Wikimedia\ObjectCache\MemcachedPhpBagOStuff { ... }; protected $processCaches = [...]; protected $logger = class MediaWiki\Logger\LegacyLogger { ... }; protected $stats = class Wikimedia\Stats\StatsFactory { ... }; protected $asyncHandler = NULL; protected $broadcastRoute = NULL; protected $useInterimHoldOffCaching = TRUE; protected $epoch = 0; protected $coalesceScheme = 2; private $tracer = class Wikimedia\Telemetry\NoopTracer { ... }; private $missLog = [...]; private $callbackDepth = 0; private $warmupCache = [...]; private $warmupKeyMisses = 0; private $wallClockOverride = NULL }]) /opt/homebrew/var/www/w/includes/filerepo/LocalRepo.php:96
PHP  12. MediaWiki\MediaWikiServices->getFileBackendGroup() /opt/homebrew/var/www/w/includes/filerepo/FileRepo.php:206
PHP  13. MediaWiki\MediaWikiServices->getService($name = 'FileBackendGroup') /opt/homebrew/var/www/w/includes/MediaWikiServices.php:1211
PHP  14. Wikimedia\Services\ServiceContainer->getService($name = 'FileBackendGroup') /opt/homebrew/var/www/w/includes/MediaWikiServices.php:381
PHP  15. Wikimedia\Services\ServiceContainer->createService($name = 'FileBackendGroup') /opt/homebrew/var/www/w/vendor/wikimedia/services/src/ServiceContainer.php:406
PHP  16. Wikimedia\Services\ServiceContainer::{closure:/opt/homebrew/var/www/w/includes/ServiceWiring.php:899-932}($services = class MediaWiki\MediaWikiServices { private ${Wikimedia\Services\ServiceContainer}services = ['BootstrapConfig' => class MediaWiki\Config\GlobalVarConfig { ... }, 'ObjectFactory' => class Wikimedia\ObjectFactory\ObjectFactory { ... }, 'HookContainer' => class MediaWiki\HookContainer\HookContainer { ... }, 'ConfigFactory' => class MediaWiki\Config\ConfigFactory { ... }, 'MainConfig' => class MediaWiki\Config\GlobalVarConfig { ... }, 'UrlUtils' => class MediaWiki\Utils\UrlUtils { ... }, 'StatsFactory' => class Wikimedia\Stats\StatsFactory { ... }, 'Tracer' => class Wikimedia\Telemetry\NoopTracer { ... }, 'ObjectCacheFactory' => class ObjectCacheFactory { ... }, 'MainWANObjectCache' => class Wikimedia\ObjectCache\WANObjectCache { ... }, 'LocalServerObjectCache' => class Wikimedia\ObjectCache\EmptyBagOStuff { ... }, 'MicroStash' => class Wikimedia\ObjectCache\MemcachedPhpBagOStuff { ... }, 'ChronologyProtector' => class Wikimedia\Rdbms\ChronologyProtector { ... }, 'CriticalSectionProvider' => class Wikimedia\RequestTimeout\CriticalSectionProvider { ... }, 'DBLoadBalancerFactoryConfigBuilder' => class MWLBFactory { ... }, 'DBLoadBalancerFactory' => class Wikimedia\Rdbms\LBFactorySimple { ... }, 'NamespaceInfo' => class MediaWiki\Title\NamespaceInfo { ... }, 'LanguageNameUtils' => class MediaWiki\Language\LanguageNameUtils { ... }, 'LocalisationCache' => class LocalisationCache { ... }, 'LanguageFallback' => class MediaWiki\Language\LanguageFallback { ... }, 'LanguageConverterFactory' => class MediaWiki\Language\LanguageConverterFactory { ... }, 'LanguageFactory' => class MediaWiki\Language\LanguageFactory { ... }, 'ContentLanguage' => class LanguageEn { ... }, 'ConnectionProvider' => class Wikimedia\Rdbms\LBFactorySimple { ... }, 'InterwikiLookup' => class MediaWiki\Interwiki\ClassicInterwikiLookup { ... }, 'TitleParser' => class MediaWiki\Title\TitleParser { ... }, 'ContentLanguageCode' => class MediaWiki\Language\LanguageCode { ... }, 'TempUserConfig' => class MediaWiki\User\TempUser\RealTempUserConfig { ... }, 'UserNameUtils' => class MediaWiki\User\UserNameUtils { ... }, 'HideUserUtils' => class MediaWiki\Block\HideUserUtils { ... }, 'ActorStoreFactory' => class MediaWiki\User\ActorStoreFactory { ... }, 'UserIdentityLookup' => class MediaWiki\User\ActorStore { ... }, 'UserFactory' => class MediaWiki\User\UserFactory { ... }, 'CentralIdLookupFactory' => class MediaWiki\User\CentralId\CentralIdLookupFactory { ... }, 'CentralIdLookup' => class MediaWiki\User\CentralId\LocalIdLookup { ... }, 'ProxyLookup' => class MediaWiki\Request\ProxyLookup { ... }, 'SessionStore' => class MediaWiki\Session\SingleBackendSessionStore { ... }, 'SessionManager' => class MediaWiki\Session\SessionManager { ... }, 'MimeAnalyzer' => class Wikimedia\Mime\MimeAnalyzer { ... }, 'RepoGroup' => class MediaWiki\FileRepo\RepoGroup { ... }, 'ReadOnlyMode' => class Wikimedia\Rdbms\ReadOnlyMode { ... }, 'LockManagerGroupFactory' => class MediaWiki\FileBackend\LockManager\LockManagerGroupFactory { ... }, 'TempFSFileFactory' => class Wikimedia\FileBackend\FSFile\TempFSFileFactory { ... }]; private ${Wikimedia\Services\ServiceContainer}serviceInstantiators = ['BootstrapConfig' => class Closure { ... }, 'Minerva.LanguagesHelper' => class Closure { ... }, 'Minerva.Menu.Definitions' => class Closure { ... }, 'Minerva.Menu.PageActions' => class Closure { ... }, 'Minerva.Permissions' => class Closure { ... }, 'Minerva.SkinOptions' => class Closure { ... }, 'Minerva.SkinUserPageHelper' => class Closure { ... }, 'Vector.ConfigHelper' => class Closure { ... }, 'Vector.FeatureManagerFactory' => class Closure { ... }, 'JsonConfig.ApiUtils' => class Closure { ... }, 'JsonConfig.ContentLoaderFactory' => class Closure { ... }, 'JsonConfig.GlobalJsonLinks' => class Closure { ... }, 'JsonConfig.Transformer' => class Closure { ... }, 'Chart.ChartArgumentsParser' => class Closure { ... }, 'Chart.ChartMetrics' => class Closure { ... }, 'Chart.ChartRenderer' => class Closure { ... }, 'Chart.ChartRequestValidator' => class Closure { ... }, 'Chart.ChartSourceValidator' => class Closure { ... }, 'Chart.DataPageResolver' => class Closure { ... }, 'TimedMediaHandler.TimedMediaThumbnail' => class Closure { ... }, 'MobileFrontend.AMC.Manager' => class Closure { ... }, 'MobileFrontend.AMC.Outreach' => class Closure { ... }, 'MobileFrontend.AMC.UserMode' => class Closure { ... }, 'MobileFrontend.Config' => class Closure { ... }, 'MobileFrontend.Context' => class Closure { ... }, 'MobileFrontend.FeaturesManager' => class Closure { ... }, 'MobileFrontend.UserModes' => class Closure { ... }, 'MobileFrontendContentProvider.Config' => class Closure { ... }, 'MobileFrontendContentProvider.Factory' => class Closure { ... }, 'ActionFactory' => class Closure { ... }, 'ActorMigration' => class Closure { ... }, 'ActorNormalization' => class Closure { ... }, 'ActorStore' => class Closure { ... }, 'ActorStoreFactory' => class Closure { ... }, 'ArchivedRevisionLookup' => class Closure { ... }, 'AuthManager' => class Closure { ... }, 'AutoblockExemptionList' => class Closure { ... }, 'BacklinkCacheFactory' => class Closure { ... }, 'BadFileLookup' => class Closure { ... }, 'BlobStore' => class Closure { ... }, 'BlobStoreFactory' => class Closure { ... }, 'BlockActionInfo' => class Closure { ... }, 'BlockErrorFormatter' => class Closure { ... }, 'BlockManager' => class Closure { ... }, 'BlockPermissionCheckerFactory' => class Closure { ... }, 'BlockRestrictionStore' => class Closure { ... }, 'BlockRestrictionStoreFactory' => class Closure { ... }, 'BlockTargetFactory' => class Closure { ... }, 'BlockUserFactory' => class Closure { ... }, 'BlockUtils' => class Closure { ... }, 'BlockUtilsFactory' => class Closure { ... }, 'BotPasswordStore' => class Closure { ... }, 'CentralIdLookup' => class Closure { ... }, 'CentralIdLookupFactory' => class Closure { ... }, 'ChangeTagDefStore' => class Closure { ... }, 'ChangeTagsStore' => class Closure { ... }, 'ChronologyProtector' => class Closure { ... }, 'CollationFactory' => class Closure { ... }, 'CommentFormatter' => class Closure { ... }, 'CommentParserFactory' => class Closure { ... }, 'CommentStore' => class Closure { ... }, 'ConfigFactory' => class Closure { ... }, 'ConfigRepository' => class Closure { ... }, 'ConfigSchema' => class Closure { ... }, 'ConfiguredReadOnlyMode' => class Closure { ... }, 'ConnectionProvider' => class Closure { ... }, 'ContentHandlerFactory' => class Closure { ... }, 'ContentJsonCodec' => class Closure { ... }, 'ContentLanguage' => class Closure { ... }, 'ContentLanguageCode' => class Closure { ... }, 'ContentModelChangeFactory' => class Closure { ... }, 'ContentModelStore' => class Closure { ... }, 'ContentRenderer' => class Closure { ... }, 'ContentTransformer' => class Closure { ... }, 'CriticalSectionProvider' => class Closure { ... }, 'CrossWikiBlockTargetFactory' => class Closure { ... }, 'DatabaseBlockStore' => class Closure { ... }, 'DatabaseBlockStoreFactory' => class Closure { ... }, 'DatabaseFactory' => class Closure { ... }, 'DateFormatterFactory' => class Closure { ... }, 'DBLoadBalancer' => class Closure { ... }, 'DBLoadBalancerFactory' => class Closure { ... }, 'DBLoadBalancerFactoryConfigBuilder' => class Closure { ... }, 'DefaultOutputPipeline' => class Closure { ... }, 'DeletePageFactory' => class Closure { ... }, 'DomainEventDispatcher' => class Closure { ... }, 'DomainEventSource' => class Closure { ... }, 'Emailer' => class Closure { ... }, 'EmailUserFactory' => class Closure { ... }, 'EventRelayerGroup' => class Closure { ... }, 'ExtensionRegistry' => class Closure { ... }, 'ExternalStoreAccess' => class Closure { ... }, 'ExternalStoreFactory' => class Closure { ... }, 'FeatureShutdown' => class Closure { ... }, 'FileBackendGroup' => class Closure { ... }, 'FormatterFactory' => class Closure { ... }, 'GenderCache' => class Closure { ... }, 'GlobalIdGenerator' => class Closure { ... }, 'GrantsInfo' => class Closure { ... }, 'GrantsLocalization' => class Closure { ... }, 'GroupPermissionsLookup' => class Closure { ... }, 'HideUserUtils' => class Closure { ... }, 'HookContainer' => class Closure { ... }, 'HtmlCacheUpdater' => class Closure { ... }, 'HtmlTransformFactory' => class Closure { ... }, 'HttpRequestFactory' => class Closure { ... }, 'InterwikiLookup' => class Closure { ... }, 'IntroMessageBuilder' => class Closure { ... }, 'JobFactory' => class Closure { ... }, 'JobQueueGroup' => class Closure { ... }, 'JobQueueGroupFactory' => class Closure { ... }, 'JobRunner' => class Closure { ... }, 'JsonCodec' => class Closure { ... }, 'JwtCodec' => class Closure { ... }, 'LanguageConverterFactory' => class Closure { ... }, 'LanguageFactory' => class Closure { ... }, 'LanguageFallback' => class Closure { ... }, 'LanguageNameUtils' => class Closure { ... }, 'LeximorphFactory' => class Closure { ... }, 'LinkBatchFactory' => class Closure { ... }, 'LinkCache' => class Closure { ... }, 'LinkRenderer' => class Closure { ... }, 'LinkRendererFactory' => class Closure { ... }, 'LinksMigration' => class Closure { ... }, 'LinkTargetLookup' => class Closure { ... }, 'LintErrorChecker' => class Closure { ... }, 'LocalisationCache' => class Closure { ... }, 'LocalServerObjectCache' => class Closure { ... }, ...]; private ${Wikimedia\Services\ServiceContainer}serviceManipulators = []; private ${Wikimedia\Services\ServiceContainer}disabled = []; private ${Wikimedia\Services\ServiceContainer}extraInstantiationParams = []; private ${Wikimedia\Services\ServiceContainer}destroyed = FALSE; private ${Wikimedia\Services\ServiceContainer}servicesBeingCreated = ['FileBackendGroup' => TRUE]; private bool $storageDisabled = FALSE }) /opt/homebrew/var/www/w/vendor/wikimedia/services/src/ServiceContainer.php:440
PHP  17. MediaWiki\FileBackend\FileBackendGroup->__construct($options = class MediaWiki\Config\ServiceOptions { private $keys = [0 => 'DirectoryMode', 1 => 'FileBackends', 2 => 'ForeignFileRepos', 3 => 'LocalFileRepo', 4 => 'fallbackWikiId']; private $options = ['DirectoryMode' => 511, 'FileBackends' => [...], 'ForeignFileRepos' => [...], 'LocalFileRepo' => [...], 'fallbackWikiId' => 'my_wiki'] }, $readOnlyMode = class Wikimedia\Rdbms\ReadOnlyMode { private Wikimedia\Rdbms\ConfiguredReadOnlyMode $configuredReadOnly = class Wikimedia\Rdbms\ConfiguredReadOnlyMode { private $reason = NULL; private $reasonFile = NULL }; private Wikimedia\Rdbms\ILBFactory $lbFactory = class Wikimedia\Rdbms\LBFactorySimple { private ${Wikimedia\Rdbms\LBFactory}csProvider = class Wikimedia\RequestTimeout\CriticalSectionProvider { ... }; private ${Wikimedia\Rdbms\LBFactory}profiler = NULL; private ${Wikimedia\Rdbms\LBFactory}trxProfiler = class Wikimedia\Rdbms\TransactionProfiler { ... }; private ${Wikimedia\Rdbms\LBFactory}tracer = class Wikimedia\Telemetry\NoopTracer { ... }; private ${Wikimedia\Rdbms\LBFactory}statsFactory = class Wikimedia\Stats\StatsFactory { ... }; private ${Wikimedia\Rdbms\LBFactory}logger = class MediaWiki\Logger\LegacyLogger { ... }; private ${Wikimedia\Rdbms\LBFactory}errorLogger = [...]; private ${Wikimedia\Rdbms\LBFactory}deprecationLogger = [...]; protected $chronologyProtector = class Wikimedia\Rdbms\ChronologyProtector { ... }; protected $srvCache = class Wikimedia\ObjectCache\HashBagOStuff { ... }; protected $wanCache = class Wikimedia\ObjectCache\WANObjectCache { ... }; protected $localDomain = class Wikimedia\Rdbms\DatabaseDomain { ... }; private ${Wikimedia\Rdbms\LBFactory}cliMode = TRUE; private ${Wikimedia\Rdbms\LBFactory}agent = ''; private ${Wikimedia\Rdbms\LBFactory}tableAliases = [...]; protected $domainAliases = [...]; protected array $virtualDomainsMapping = [...]; protected array $virtualDomains = [...]; private ${Wikimedia\Rdbms\LBFactory}replicationWaitCallbacks = [...]; private ${Wikimedia\Rdbms\LBFactory}ticket = 1092216334; private ${Wikimedia\Rdbms\LBFactory}trxRoundFname = NULL; private ${Wikimedia\Rdbms\LBFactory}trxRoundStage = 'cursory'; private ${Wikimedia\Rdbms\LBFactory}replicationWaitTimeout = 60; protected $readOnlyReason = FALSE; private ${Wikimedia\Rdbms\LBFactory}defaultGroup = NULL; private ${Wikimedia\Rdbms\LBFactory}configCallback = NULL; private $mainLB = class Wikimedia\Rdbms\LoadBalancer { ... }; private $externalLBs = [...]; private $loadMonitorConfig = [...]; private $mainServers = [...]; private $externalServersByCluster = [...] } }, $srvCache = class Wikimedia\ObjectCache\HashBagOStuff { protected $stats = class Wikimedia\Stats\StatsFactory { private string $component = ''; private Wikimedia\Stats\StatsCache $cache = class Wikimedia\Stats\StatsCache { ... }; private Wikimedia\Stats\Emitters\EmitterInterface $emitter = class Wikimedia\Stats\Emitters\NullEmitter { ... }; private Psr\Log\LoggerInterface $logger = class Psr\Log\NullLogger { ... }; private ?Wikimedia\Stats\IBufferingStatsdDataFactory $statsdDataFactory = NULL }; protected $logger = class Psr\Log\NullLogger {  }; protected $asyncHandler = NULL; protected $attrMap = [2 => 2]; protected $keyspace = 'local'; protected $lastError = 0; protected $lastErrorId = 0; private ${Wikimedia\ObjectCache\BagOStuff}wallClockOverride = NULL; protected $locks = []; protected $segmentationSize = INF; protected $segmentedValueMaxSize = 67108864; protected $maxLockSendDelay = 0.05; private ${Wikimedia\ObjectCache\MediumSpecificBagOStuff}duplicateKeyLookups = []; private ${Wikimedia\ObjectCache\MediumSpecificBagOStuff}reportDupes = FALSE; private ${Wikimedia\ObjectCache\MediumSpecificBagOStuff}dupeTrackScheduled = FALSE; protected $bag = []; protected $maxCacheKeys = INF; private $token = '1758132496.1699:942883116' }, $wanCache = class Wikimedia\ObjectCache\WANObjectCache { protected $cache = class Wikimedia\ObjectCache\MemcachedPhpBagOStuff { protected $stats = class Wikimedia\Stats\StatsFactory { ... }; protected $logger = class MediaWiki\Logger\LegacyLogger { ... }; protected $asyncHandler = [...]; protected $attrMap = [...]; protected $keyspace = 'my_wiki'; protected $lastError = 0; protected $lastErrorId = 0; private ${Wikimedia\ObjectCache\BagOStuff}wallClockOverride = NULL; protected $locks = [...]; protected $segmentationSize = 917504; protected $segmentedValueMaxSize = 67108864; protected $maxLockSendDelay = 0.05; private ${Wikimedia\ObjectCache\MediumSpecificBagOStuff}duplicateKeyLookups = [...]; private ${Wikimedia\ObjectCache\MediumSpecificBagOStuff}reportDupes = TRUE; private ${Wikimedia\ObjectCache\MediumSpecificBagOStuff}dupeTrackScheduled = FALSE; protected $routingPrefix = ''; protected $client = class MemcachedClient { ... } }; protected $processCaches = ['primary:1000' => class Wikimedia\MapCacheLRU\MapCacheLRU { ... }]; protected $logger = class MediaWiki\Logger\LegacyLogger { protected $channel = 'memcached'; private $minimumLevel = 999; private $isDB = FALSE }; protected $stats = class Wikimedia\Stats\StatsFactory { private string $component = ''; private Wikimedia\Stats\StatsCache $cache = class Wikimedia\Stats\StatsCache { ... }; private Wikimedia\Stats\Emitters\EmitterInterface $emitter = class Wikimedia\Stats\Emitters\NullEmitter { ... }; private Psr\Log\LoggerInterface $logger = class Psr\Log\NullLogger { ... }; private ?Wikimedia\Stats\IBufferingStatsdDataFactory $statsdDataFactory = NULL }; protected $asyncHandler = NULL; protected $broadcastRoute = NULL; protected $useInterimHoldOffCaching = TRUE; protected $epoch = 0; protected $coalesceScheme = 2; private $tracer = class Wikimedia\Telemetry\NoopTracer { private Wikimedia\Telemetry\SpanContext $noopSpanContext = class Wikimedia\Telemetry\SpanContext { ... }; private Wikimedia\Telemetry\TracerState $tracerState = class Wikimedia\Telemetry\TracerState { ... }; private ?Wikimedia\Telemetry\ContextPropagatorInterface $contextPropagator = class MediaWiki\Telemetry\MediaWikiPropagator { ... } }; private $missLog = [0 => [...], 1 => [...], 2 => [...], 3 => [...], 4 => [...], 5 => [...], 6 => [...], 7 => [...], 8 => [...], 9 => [...]]; private $callbackDepth = 0; private $warmupCache = []; private $warmupKeyMisses = 0; private $wallClockOverride = NULL }, $mimeAnalyzer = class Wikimedia\Mime\MimeAnalyzer { protected $typeFile = 'internal'; protected $infoFile = 'internal'; protected $xmlTypes = ['http://www.w3.org/2000/svg:svg' => 'image/svg+xml', 'svg' => 'image/svg+xml', 'http://www.lysator.liu.se/~alla/dia/:diagram' => 'application/x-dia-diagram', 'http://www.w3.org/1999/xhtml:html' => 'text/html', 'html' => 'text/html']; protected $initCallback = class Closure { public $name = '{closure:{closure:/opt/homebrew/var/www/w/includes/ServiceWiring.php:1421}:1448}'; public $file = '/opt/homebrew/var/www/w/includes/ServiceWiring.php'; public $line = 1448; public $static = [...]; public $parameter = [...] }; protected $detectCallback = NULL; protected $guessCallback = class Closure { public $name = '{closure:{closure:/opt/homebrew/var/www/w/includes/ServiceWiring.php:1421}:1429}'; public $file = '/opt/homebrew/var/www/w/includes/ServiceWiring.php'; public $line = 1429; public $static = [...]; public $parameter = [...] }; protected $extCallback = class Closure { public $name = '{closure:{closure:/opt/homebrew/var/www/w/includes/ServiceWiring.php:1421}:1444}'; public $file = '/opt/homebrew/var/www/w/includes/ServiceWiring.php'; public $line = 1444; public $static = [...]; public $parameter = [...] }; protected $mediaTypes = ['OFFICE' => [...], 'EXECUTABLE' => [...], 'MULTIMEDIA' => [...], 'AUDIO' => [...], 'BITMAP' => [...], 'DRAWING' => [...], 'TEXT' => [...], 'VIDEO' => [...], 'UNKNOWN' => [...], 'ARCHIVE' => [...], '3D' => [...]]; protected $mimeTypeAliases = ['text/javascript' => 'application/javascript', 'application/x-javascript' => 'application/javascript', 'audio/mpeg' => 'audio/mp3', 'audio/ogg' => 'application/ogg', 'video/ogg' => 'application/ogg', 'image/x-ms-bmp' => 'image/x-bmp', 'image/bmp' => 'image/x-bmp', 'application/octet-stream' => 'unknown/unknown', 'application/x-empty' => 'unknown/unknown', 'image/x-png' => 'image/png', 'image/pjpeg' => 'image/jpeg', 'image/x-ico' => 'image/x-icon', 'image/vnd.microsoft.icon' => 'image/x-icon', 'image/x-portable-greymap' => 'image/x-portable-graymap', 'application/x-bmp' => 'image/x-bmp', 'application/bmp' => 'image/x-bmp', 'image/psd' => 'image/x-photoshop', 'image/x-psd' => 'image/x-photoshop', 'image/photoshop' => 'image/x-photoshop', 'image/vnd.adobe.photoshop' => 'image/x-photoshop', 'application/svg+xml' => 'image/svg+xml', 'application/svg' => 'image/svg+xml', 'image/svg' => 'image/svg+xml', 'audio/mp3' => 'audio/mpeg', 'audio/mpeg3' => 'audio/mpeg', 'audio/x-wav' => 'audio/wav', 'audio/wave' => 'audio/wav', 'audio/mid' => 'audio/midi', 'application/mpeg' => 'video/mpeg', 'application/x-ogg' => 'application/ogg', 'audio/x-ogg' => 'application/ogg', 'video/x-ogg' => 'application/ogg', 'application/xhtml+xml' => 'text/html', 'text/xml' => 'application/xml', 'application/x-zip' => 'application/zip', 'application/x-ecmascript' => 'application/javascript', 'text/ecmascript' => 'application/javascript', 'application/acrobat' => 'application/pdf', 'image/x.djvu' => 'image/vnd.djvu', 'image/x-djvu' => 'image/vnd.djvu', 'application/x-acad' => 'application/acad', 'application/autocad_dwg' => 'application/acad', 'image/x-dwg' => 'application/acad', 'application/dwg' => 'application/acad', 'application/x-dwg' => 'application/acad', 'application/x-autocad' => 'application/acad', 'image/vnd.dwg' => 'application/acad', 'drawing/dwg' => 'application/acad', 'image/jpeg2000' => 'image/jp2', 'image/jpeg2000-image' => 'image/jp2', 'image/x-jpeg2000-image' => 'image/jp2', 'application/csv' => 'text/csv', 'application/x-csv' => 'text/csv', 'text/x-csv' => 'text/csv', 'text/comma-separated-values' => 'text/csv', 'text/x-comma-separated-values' => 'text/csv']; protected $mimeToExts = ['application/ogg' => [...], 'application/pdf' => [...], 'application/vnd.apple.mpegurl' => [...], 'application/vnd.ms-opentype' => [...], 'application/vnd.oasis.opendocument.chart' => [...], 'application/vnd.oasis.opendocument.chart-template' => [...], 'application/vnd.oasis.opendocument.database' => [...], 'application/vnd.oasis.opendocument.formula' => [...], 'application/vnd.oasis.opendocument.formula-template' => [...], 'application/vnd.oasis.opendocument.graphics' => [...], 'application/vnd.oasis.opendocument.graphics-template' => [...], 'application/vnd.oasis.opendocument.image' => [...], 'application/vnd.oasis.opendocument.image-template' => [...], 'application/vnd.oasis.opendocument.presentation' => [...], 'application/vnd.oasis.opendocument.presentation-template' => [...], 'application/vnd.oasis.opendocument.spreadsheet' => [...], 'application/vnd.oasis.opendocument.spreadsheet-template' => [...], 'application/vnd.oasis.opendocument.text' => [...], 'application/vnd.oasis.opendocument.text-master' => [...], 'application/vnd.oasis.opendocument.text-template' => [...], 'application/vnd.oasis.opendocument.text-web' => [...], 'application/javascript' => [...], 'application/x-mpegurl' => [...], 'application/x-shockwave-flash' => [...], 'audio/midi' => [...], 'audio/mpeg' => [...], 'audio/x-aiff' => [...], 'audio/x-wav' => [...], 'audio/ogg' => [...], 'audio/opus' => [...], 'image/x-bmp' => [...], 'image/gif' => [...], 'image/jpeg' => [...], 'image/png' => [...], 'image/svg+xml' => [...], 'image/svg' => [...], 'image/tiff' => [...], 'image/vnd.djvu' => [...], 'image/x.djvu' => [...], 'image/x-djvu' => [...], 'image/x-portable-pixmap' => [...], 'image/x-xcf' => [...], 'text/plain' => [...], 'text/html' => [...], 'video/ogg' => [...], 'video/mpeg' => [...], 'application/acad' => [...], 'application/andrew-inset' => [...], 'application/mac-binhex40' => [...], 'application/mac-compactpro' => [...], 'application/mathml+xml' => [...], 'application/msword' => [...], 'application/octet-stream' => [...], 'application/oda' => [...], 'application/postscript' => [...], 'application/rdf+xml' => [...], 'application/smil' => [...], 'application/srgs' => [...], 'application/srgs+xml' => [...], 'application/vnd.mif' => [...], 'application/vnd.ms-excel' => [...], 'application/vnd.ms-powerpoint' => [...], 'application/vnd.wap.wbxml' => [...], 'application/vnd.wap.wmlc' => [...], 'application/vnd.wap.wmlscriptc' => [...], 'application/voicexml+xml' => [...], 'application/x-7z-compressed' => [...], 'application/x-bcpio' => [...], 'application/x-bzip' => [...], 'application/x-bzip2' => [...], 'application/x-cdlink' => [...], 'application/x-chess-pgn' => [...], 'application/x-cpio' => [...], 'application/x-csh' => [...], 'application/x-dia-diagram' => [...], 'application/x-director' => [...], 'application/x-dvi' => [...], 'application/x-futuresplash' => [...], 'application/x-gtar' => [...], 'application/x-gzip' => [...], 'application/x-hdf' => [...], 'application/x-jar' => [...], 'application/json' => [...], 'application/x-koan' => [...], 'application/x-latex' => [...], 'application/x-netcdf' => [...], 'application/x-sh' => [...], 'application/x-shar' => [...], 'application/x-stuffit' => [...], 'application/x-sv4cpio' => [...], 'application/x-sv4crc' => [...], 'application/x-tar' => [...], 'application/x-tcl' => [...], 'application/x-tex' => [...], 'application/x-texinfo' => [...], 'application/x-troff' => [...], 'application/x-troff-man' => [...], 'application/x-troff-me' => [...], 'application/x-troff-ms' => [...], 'application/x-ustar' => [...], 'application/x-wais-source' => [...], 'application/x-xpinstall' => [...], 'application/xhtml+xml' => [...], 'application/xslt+xml' => [...], 'application/xml' => [...], 'application/xml-dtd' => [...], 'application/zip' => [...], 'application/x-rar' => [...], 'font/collection' => [...], 'font/otf' => [...], 'font/sfnt' => [...], 'font/ttf' => [...], 'application/font-sfnt' => [...], 'font/woff' => [...], 'application/font-woff' => [...], 'font/woff2' => [...], 'application/font-woff2' => [...], 'application/vnd.ms-fontobject' => [...], 'application/x-font-ttf' => [...], 'audio/basic' => [...], 'video/webm' => [...], 'audio/webm' => [...], 'audio/x-matroska' => [...], 'audio/x-mpegurl' => [...], 'audio/x-ogg' => [...], 'audio/x-pn-realaudio' => [...], 'audio/x-pn-realaudio-plugin' => [...], 'audio/x-realaudio' => [...], ...]; protected $extToMimes = ['ogx' => [...], 'ogg' => [...], 'ogm' => [...], 'ogv' => [...], 'oga' => [...], 'spx' => [...], 'opus' => [...], 'pdf' => [...], 'm3u8' => [...], 'm3u' => [...], 'otf' => [...], 'odc' => [...], 'otc' => [...], 'odb' => [...], 'odf' => [...], 'odg' => [...], 'otg' => [...], 'odi' => [...], 'oti' => [...], 'odp' => [...], 'otp' => [...], 'ods' => [...], 'ots' => [...], 'odt' => [...], 'otm' => [...], 'odm' => [...], 'ott' => [...], 'oth' => [...], 'js' => [...], 'swf' => [...], 'mid' => [...], 'midi' => [...], 'kar' => [...], 'mpga' => [...], 'mpa' => [...], 'mp2' => [...], 'mp3' => [...], 'aif' => [...], 'aiff' => [...], 'aifc' => [...], 'wav' => [...], 'bmp' => [...], 'gif' => [...], 'jpeg' => [...], 'jpg' => [...], 'jpe' => [...], 'jps' => [...], 'png' => [...], 'apng' => [...], 'svg' => [...], 'tiff' => [...], 'tif' => [...], 'djvu' => [...], 'djv' => [...], 'ppm' => [...], 'xcf' => [...], 'txt' => [...], 'html' => [...], 'htm' => [...], 'mpg' => [...], 'mpeg' => [...], 'mpe' => [...], 'dwg' => [...], 'ez' => [...], 'hqx' => [...], 'cpt' => [...], 'mathml' => [...], 'doc' => [...], 'dot' => [...], 'bin' => [...], 'dms' => [...], 'lha' => [...], 'lzh' => [...], 'exe' => [...], 'class' => [...], 'so' => [...], 'dll' => [...], 'oda' => [...], 'ai' => [...], 'eps' => [...], 'ps' => [...], 'rdf' => [...], 'owl' => [...], 'smi' => [...], 'smil' => [...], 'gram' => [...], 'grxml' => [...], 'mif' => [...], 'xls' => [...], 'xlt' => [...], 'xla' => [...], 'ppt' => [...], 'pot' => [...], 'pps' => [...], 'ppa' => [...], 'wbxml' => [...], 'wmlc' => [...], 'wmlsc' => [...], 'vxml' => [...], '7z' => [...], 'bcpio' => [...], 'bz' => [...], 'bz2' => [...], 'vcd' => [...], 'pgn' => [...], 'cpio' => [...], 'csh' => [...], 'dia' => [...], 'dcr' => [...], 'dir' => [...], 'dxr' => [...], 'dvi' => [...], 'spl' => [...], 'gtar' => [...], 'tar' => [...], 'gz' => [...], 'hdf' => [...], 'jar' => [...], 'json' => [...], 'skp' => [...], 'skd' => [...], 'skt' => [...], 'skm' => [...], 'latex' => [...], 'nc' => [...], 'cdf' => [...], 'sh' => [...], 'shar' => [...], ...]; public $mExtToMime = []; private $extraTypes = ''; private $extraInfo = ''; private $logger = class MediaWiki\Logger\LegacyLogger { protected $channel = 'Mime'; private $minimumLevel = 999; private $isDB = FALSE } }, $lmgFactory = class MediaWiki\FileBackend\LockManager\LockManagerGroupFactory { private $defaultDomain = 'my_wiki'; private $lockManagerConfigs = [0 => [...], 1 => [...]]; private $instances = [] }, $tmpFileFactory = class Wikimedia\FileBackend\FSFile\TempFSFileFactory { private $tmpDirectory = '/var/folders/59/d74t3m7d7l5c_18w4d_8fync0000gp/T/' }, $objectFactory = class Wikimedia\ObjectFactory\ObjectFactory { protected Psr\Container\ContainerInterface $serviceContainer = class MediaWiki\MediaWikiServices { private ${Wikimedia\Services\ServiceContainer}services = [...]; private ${Wikimedia\Services\ServiceContainer}serviceInstantiators = [...]; private ${Wikimedia\Services\ServiceContainer}serviceManipulators = [...]; private ${Wikimedia\Services\ServiceContainer}disabled = [...]; private ${Wikimedia\Services\ServiceContainer}extraInstantiationParams = [...]; private ${Wikimedia\Services\ServiceContainer}destroyed = FALSE; private ${Wikimedia\Services\ServiceContainer}servicesBeingCreated = [...]; private bool $storageDisabled = FALSE } }) /opt/homebrew/var/www/w/includes/ServiceWiring.php:921

Details

Event Timeline

HSwan-WMF moved this task from Incoming/Inbox to Backlog on the Reader Growth Team board.

Change #1191739 had a related patch set uploaded (by Bvibber; author: Bvibber):

[mediawiki/core@master] Avoid PHP warning when using a ForeignAPIRepo

https://gerrit.wikimedia.org/r/1191739

Whipped up a provisional patch which I _think_ works correctly, but I'm not super familiar with the current guts of the file backend setups so I'd love some code review.

If we find this config affecting PatchDemo we can push this harder or find a workaround. :)

Can repro if we point to Commons too, my local development wiki has this:

///                           
/// InstantCommons with local thumb caching
///
                                                     
$wgForeignFileRepos[] = [
        'class' => ForeignAPIRepo::class,
        'name' => 'commonswiki',
        'apibase' => 'https://commons.wikimedia.org/w/api.php',
        'hashLevels' => 2,
        'fetchDescription' => true,
        'descriptionCacheExpiry' => 43200, // 12h
        'apiThumbCacheExpiry' => 86400, // 24h
];

It's not only a warning: thumbnails aren't stored on disk.

The warning is raised during web requests too I see lot of this in my php-fpm log, in addition to the update maintenance script warning:

[25-Oct-2025 00:15:04 UTC] PHP Warning: Undefined array key "directory" in /var/51-wwwroot/mediawiki-dereckson/core/includes/filebackend/FileBackendGroup.php on line 119

It's not only a warning: thumbnails aren't stored on disk.

not much changed in filebackend... so this was always broken then ?

thumbnails aren't stored on disk.

you mean when not using url+thumbUrl AND not specifying directory ? Those seem like mutual exclusive options... but it seems nothing enforces this....

strange...
includes/SetupDynamicConfig.php has the following, so that means we are not hitting that code ??

foreach ( $wgForeignFileRepos as &$repo ) {
        if ( !isset( $repo['directory'] ) && $repo['class'] === ForeignAPIRepo::class ) {
                $repo['directory'] = $wgUploadDirectory; // b/c
        }
        if ( !isset( $repo['backend'] ) ) {
                $repo['backend'] = $repo['name'] . '-backend';
        }
}

[edit] this should probably also check against IForeignRepoWithMWApi instead of ForeignAPIRepo::class .....