Skip to content

[Portal Engine] Direct Edit status EventSource is never closed and swallows every error #353

Description

@fashxp

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

  1. Install Portal Engine together with Direct Edit and a running Mercure hub.
  2. Log into a portal and open an asset in a data pool, so the asset detail view renders.
  3. Navigate to another asset, and another, so the detail view mounts several times.
  4. 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    Affected capability

    None yet

    Platform Version

    None yet

    Galaxy

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions