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
{{ message }}
Repository navigation
[Generic Execution Engine] PermissionService starts the session in its constructor, closing the EntityManager during streamed responses #616
Verified on pimcore/pimcore v2026.3.0; PermissionService is unchanged on 2026.x.
Have a frontend controller that returns a StreamedResponse (for example server-sent events) and writes a Doctrine entity while streaming, after the first chunk has been flushed. The entity has nothing to do with the execution engine.
Call it with any session cookie on the request (Cookie: PHPSESSID=<anything>), e.g. from a browser that is logged into the Pimcore admin on the same host.
The first Doctrine preUpdate of the request makes the ContainerAwareEventManager build its preUpdate listeners, among them GenericExecutionEngineBundle\EventSubscriber\JobRunStatusChangeSubscriber.
The same request without the session cookie works.
Actual Behavior
Building JobRunStatusChangeSubscriber builds JobRunRepository, which builds GenericExecutionEngineBundle\Security\PermissionService. Its constructor calls Pimcore\Tool\Authentication::authenticateSession(), which reads _security_pimcore_admin from the session. With a previous session on the request this starts the PHP session. The response has already sent its headers, so NativeSessionStorage::start() throws:
RuntimeException: Failed to start the session because headers have already been sent by "..." at line ...
The exception is thrown inside EntityManager::flush(), so Doctrine closes the EntityManager and every later write of the request fails with The EntityManager is closed.
Writing an unrelated entity must not touch the session. JobRunStatusChangeSubscriber listens to preUpdate of every entity and only filters for JobRun inside, so its dependency chain, and with it the session read, is built in any request that updates any entity.
Suggested fix: resolve the user in PermissionService on first use instead of in the constructor (a lazy getter used by isAllowedToSeeJobRuns() / isAllowedToSeeAllJobRuns() and the other checks). Alternatively, or additionally, register JobRunStatusChangeSubscriber as an entity listener for JobRun only (#[AsEntityListener(entity: JobRun::class)]), so it is not built for other entities at all.
Related: #486 (same subscriber closing the EntityManager inside preUpdate, different trigger).
Activity
[Generic Execution Engine] Resolve the PermissionService user on firs…
kingjia90 commentedon Oct 16, 2026
Resolved by pimcore/pimcore#19569