Affected Version
2026.2 (pimcore/portal-engine v2026.2.3, pimcore/direct-edit v2026.2.2). The component is
unchanged on the other supported lines.
Affected capability
Portal-Engine
Steps to reproduce
- Install Portal Engine together with Direct Edit and a running Mercure hub.
- Log into a portal and open an asset in a data pool, so the asset detail view renders.
- Navigate to another asset, and another, so the detail view mounts several times.
- Watch the hub:
docker compose logs mercure | grep "New subscriber".
For the error handling, instead: stop the Mercure hub (or configure it to reject unauthorized
subscribers) and open an asset detail view.
Actual Behavior
assets/scripts/features/asset/components/detail/DirectEditStatus.js opens a raw EventSource for
the Direct Edit status indicator:
useEffect(() => {
const mercureUrl = getConfig("directEdit.mercureUrl");
if(mercureUrl) {
const url = new URL(mercureUrl);
url.searchParams.append("topic", 'http://www.pimcore.com/direct-edit/client-upload/user/' + getConfig("directEdit.userId"));
const eventSource = new EventSource(url);
eventSource.onmessage = event => { /* ... */ }
eventSource.onopen = event => {};
eventSource.onabort = event => {};
eventSource.onerror = event => {};
}
}, []);
Three problems:
1. The subscription is never closed. The effect returns no cleanup function, so the
EventSource outlives the component. Each mount of the asset detail view leaves another open hub
subscription behind for the lifetime of the page; an open EventSource with a live connection is not
collected. In an SPA that mounts this view once per asset the user opens, the subscriptions
accumulate.
2. Every failure is swallowed. onerror is an empty function, so a refused, dropped or
never-established subscription is completely invisible: the status indicator simply never updates
while the browser retry-loops. There is no reconnect handling and no user-visible state for "the
live channel is down".
3. onabort is not an EventSource event. EventSource has no abort event (that is
XMLHttpRequest), so this handler is dead code.
There is also no authorization on this subscription at all - see #351. Portal users authenticate as
PortalUser and never receive Studio's mercureAuthorization cookie, and Direct Edit hands the
browser no token, so the subscription works only because the hub is configured to accept anonymous
subscribers and the updates are published public. Problem 2 is what makes that failure mode silent:
against a hub without anonymous, this gets 401 and nothing anywhere says so.
Verified by reading the component and the surrounding wiring
(Controller/DataPool/AssetController.php passes directEdit.mercureUrl and the prefixed portal
user id into the frontend config); the accumulation was not reproduced in a browser.
Expected Behavior
The first two are worth fixing independently of #351.
Related: #351 (Direct Edit's classic channel has no authorization), #346 (the same authorization
model on the Studio side, fixed by pimcore/studio-ui-bundle#4009 and
pimcore/studio-backend-bundle#2012).
Affected Version
2026.2 (
pimcore/portal-enginev2026.2.3,pimcore/direct-editv2026.2.2). The component isunchanged on the other supported lines.
Affected capability
Portal-Engine
Steps to reproduce
docker compose logs mercure | grep "New subscriber".For the error handling, instead: stop the Mercure hub (or configure it to reject unauthorized
subscribers) and open an asset detail view.
Actual Behavior
assets/scripts/features/asset/components/detail/DirectEditStatus.jsopens a rawEventSourceforthe Direct Edit status indicator:
Three problems:
1. The subscription is never closed. The effect returns no cleanup function, so the
EventSourceoutlives the component. Each mount of the asset detail view leaves another open hubsubscription behind for the lifetime of the page; an open EventSource with a live connection is not
collected. In an SPA that mounts this view once per asset the user opens, the subscriptions
accumulate.
2. Every failure is swallowed.
onerroris an empty function, so a refused, dropped ornever-established subscription is completely invisible: the status indicator simply never updates
while the browser retry-loops. There is no reconnect handling and no user-visible state for "the
live channel is down".
3.
onabortis not anEventSourceevent.EventSourcehas noabortevent (that isXMLHttpRequest), so this handler is dead code.There is also no authorization on this subscription at all - see #351. Portal users authenticate as
PortalUserand never receive Studio'smercureAuthorizationcookie, and Direct Edit hands thebrowser no token, so the subscription works only because the hub is configured to accept anonymous
subscribers and the updates are published public. Problem 2 is what makes that failure mode silent:
against a hub without
anonymous, this gets401and nothing anywhere says so.Verified by reading the component and the surrounding wiring
(
Controller/DataPool/AssetController.phppassesdirectEdit.mercureUrland the prefixed portaluser id into the frontend config); the accumulation was not reproduced in a browser.
Expected Behavior
EventSourcein auseEffectcleanup, so a subscription lives exactly as long as thecomponent that opened it.
onerror: surface that the live channel is down, and reconnect rather than relying on thebrowser's implicit retry. Drop the
onaborthandler.page is open. A token that lives an hour on a portal page left open all day reproduces [Studio UI] Mercure subscription silently falls back to anonymous 1 hour after page load, all live updates stop #346
exactly - the hub re-checks authorization on every reconnect, so an expired token means a
subscription that is silently anonymous (or refused) from then on.
The first two are worth fixing independently of #351.
Related: #351 (Direct Edit's classic channel has no authorization), #346 (the same authorization
model on the Studio side, fixed by pimcore/studio-ui-bundle#4009 and
pimcore/studio-backend-bundle#2012).