You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In a Theia 1.71.1 application, frontend startup hangs forever waiting on PreferenceServiceImpl.initializeProviders to settle _ready. Tracing through the runtime container:
PreferenceServiceImpl.initializeProviders awaits each scope provider's ready. It blocks on the User scope.
UserConfigsPreferenceProvider.ready is forever pending.
Its four section providers (settings.json, tasks.json, launch.json, extensions.json) are all AbstractResourcePreferenceProvider instances whose _ready is pending — they're stuck on await this.readPreferencesFromFile() inside doInit().
readPreferencesFromFile() ultimately awaits fileService.read(uri) where uri has scheme user-storage. That call awaits fileService.activateProvider('user-storage').
fileService.activations.get('user-storage') is a pending Deferred that never settles.
The activation deferred is resolved/rejected inside activateProvider() after WaitUntilEvent.fire(this.onWillActivateFileSystemProviderEmitter, { scheme }) resolves. So fire(...) is the hung promise — one of the listeners' event.waitUntil(...) promises never settles.
The puzzle: when probed from the running (hung) frontend, every component the listener depends on works in isolation:
// All of these succeed:awaitenv.getConfigDirUri();// → "file:///home/runner/.theia" (~4 ms)awaituserStorageContribution.getDelegate(fileService);// service.activateProvider('file')awaituserStorageContribution.createProvider(fileService);// the exact body of the listener's IIFE
Calling fileService.registerProvider('user-storage', providerCreatedManually) even adds the provider to fileService.providers, but the original activations Deferred for 'user-storage' remains pending. The listener's event.waitUntil(...) Promise was created at startup but never settled, even though running the same logic now completes in milliseconds.
This is the dangling-promise pattern. It looks like the same family that #17334 addressed (RPC and channel-multiplex paths), but along a different code path — WaitUntilEvent.fire / Promise.all(waitables) in FileService.activateProvider.
Steps to Reproduce:
I don't have a minimal upstream-only repro yet. The hang is consistent on every reload in our setup but I haven't been able to isolate it to examples/browser. The following diagnostic snippet, evaluated against the live container of a hung frontend, demonstrates the state and the puzzle:
constc=window.theia.container;constfs=/* the FileService instance (has .providers, .activations, .onWillActivateFileSystemProvider) */;constps=/* the PreferenceServiceImpl (has .ready, ._isReady, .preferenceProviders) */;constuserProvider=ps.providerProvider(PreferenceScope.User);// UserConfigsPreferenceProvider// PreferenceService is hung:ps._isReady;// falseawaitPromise.race([ps.ready,newPromise(r=>setTimeout(()=>r('PENDING'),500))]);// 'PENDING'// User scope provider's ready is hung:awaitPromise.race([userProvider.ready,newPromise(r=>setTimeout(()=>r('PENDING'),500))]);// 'PENDING'// All four inner section providers are pending:for(const[uri,p]ofuserProvider.providers.entries()){awaitPromise.race([p.ready,newPromise(r=>setTimeout(()=>r('PENDING'),200))]);// 'PENDING' for all}// The activation that started it all is pending:awaitPromise.race([fs.activations.get('user-storage'),newPromise(r=>setTimeout(()=>r('PENDING'),500))]);// 'PENDING'// But every component now works in isolation:awaitenv.getConfigDirUri();// → "file:///home/runner/.theia"awaituserStorageContribution.createProvider(fs);// → ProviderInstance (~4 ms)
window.theia.application is undefined — FrontendApplication.start() is hung on a contribution's onStart.
fileService.providers contains vscode and file. fileService.activations contains file (resolved) and user-storage (pending forever).
RemoteFileSystemProvider._capabilities = 50336798 (non-zero) — the backend's getCapabilities() RPC for the file scheme completed successfully, so the basic RPC layer is healthy.
onWillActivateFileSystemProviderEmitter has 5 listeners registered when the hang occurs, so UserStorageContribution.registerFileSystemProviders did run before the activation was attempted.
What I ruled out:
PR fix(core): unbind all services in connection container on close #17384 (unbind all services in connection container on close): patched out the socket.onClose(() => connectionContainer.unbindAllAsync()) line in the running backend's default-messaging-service.js — hang persists identically.
Stale node_modules: verified @theia/filesystem, @theia/userstorage, @theia/preferences, @theia/core are all 1.71.1 at runtime.
Wrong injection of EnvVariablesServer:userStorageContribution.environments is the proper EnvVariablesServer proxy object; calling .getConfigDirUri() on it succeeds.
Hypothesis: a startup-time race where the event.waitUntil(asyncFn()) promise created during WaitUntilEvent.fire('user-storage') somehow never settles — possibly the IIFE's first await resolves to a state whose continuation never runs (microtask cancelled / detached). Same family as the dangling-promise bugs addressed by #17334, but on a different path. Note that in 1.71's refactored preferences code, AbstractResourcePreferenceProvider.doInit() now constructs FrontendPreferenceStorage, whose constructor synchronously calls fileService.watch(uri) — kicking off a fire-and-forget doWatch(...) → activateProvider(scheme)before the awaited readPreferencesFromFile() call. That doubled activation pressure during startup might be where the race is exposed.
Happy to provide more instrumented traces or test patch suggestions.
Bug Description:
In a Theia 1.71.1 application, frontend startup hangs forever waiting on
PreferenceServiceImpl.initializeProvidersto settle_ready. Tracing through the runtime container:PreferenceServiceImpl.initializeProvidersawaits each scope provider'sready. It blocks on theUserscope.UserConfigsPreferenceProvider.readyis forever pending.settings.json,tasks.json,launch.json,extensions.json) are allAbstractResourcePreferenceProviderinstances whose_readyis pending — they're stuck onawait this.readPreferencesFromFile()insidedoInit().readPreferencesFromFile()ultimately awaitsfileService.read(uri)whereurihas schemeuser-storage. That call awaitsfileService.activateProvider('user-storage').fileService.activations.get('user-storage')is a pendingDeferredthat never settles.The activation deferred is resolved/rejected inside
activateProvider()afterWaitUntilEvent.fire(this.onWillActivateFileSystemProviderEmitter, { scheme })resolves. Sofire(...)is the hung promise — one of the listeners'event.waitUntil(...)promises never settles.The puzzle: when probed from the running (hung) frontend, every component the listener depends on works in isolation:
Calling
fileService.registerProvider('user-storage', providerCreatedManually)even adds the provider tofileService.providers, but the originalactivationsDeferred for'user-storage'remains pending. The listener'sevent.waitUntil(...)Promise was created at startup but never settled, even though running the same logic now completes in milliseconds.This is the dangling-promise pattern. It looks like the same family that #17334 addressed (RPC and channel-multiplex paths), but along a different code path —
WaitUntilEvent.fire/Promise.all(waitables)inFileService.activateProvider.Steps to Reproduce:
I don't have a minimal upstream-only repro yet. The hang is consistent on every reload in our setup but I haven't been able to isolate it to
examples/browser. The following diagnostic snippet, evaluated against the live container of a hung frontend, demonstrates the state and the puzzle:Additional Information
State observed at the hang:
window.theia.applicationisundefined—FrontendApplication.start()is hung on a contribution'sonStart.fileService.providerscontainsvscodeandfile.fileService.activationscontainsfile(resolved) anduser-storage(pending forever).RemoteFileSystemProvider._capabilities = 50336798(non-zero) — the backend'sgetCapabilities()RPC for thefilescheme completed successfully, so the basic RPC layer is healthy.onWillActivateFileSystemProviderEmitterhas 5 listeners registered when the hang occurs, soUserStorageContribution.registerFileSystemProvidersdid run before the activation was attempted.What I ruled out:
unbind all services in connection container on close): patched out thesocket.onClose(() => connectionContainer.unbindAllAsync())line in the running backend'sdefault-messaging-service.js— hang persists identically.@theia/filesystem,@theia/userstorage,@theia/preferences,@theia/coreare all 1.71.1 at runtime.EnvVariablesServer:userStorageContribution.environmentsis the properEnvVariablesServerproxy object; calling.getConfigDirUri()on it succeeds.Hypothesis: a startup-time race where the
event.waitUntil(asyncFn())promise created duringWaitUntilEvent.fire('user-storage')somehow never settles — possibly the IIFE's firstawaitresolves to a state whose continuation never runs (microtask cancelled / detached). Same family as the dangling-promise bugs addressed by #17334, but on a different path. Note that in 1.71's refactored preferences code,AbstractResourcePreferenceProvider.doInit()now constructsFrontendPreferenceStorage, whose constructor synchronously callsfileService.watch(uri)— kicking off a fire-and-forgetdoWatch(...) → activateProvider(scheme)before the awaitedreadPreferencesFromFile()call. That doubled activation pressure during startup might be where the race is exposed.Happy to provide more instrumented traces or test patch suggestions.