Problem
When using renderPlain() to send the HTML of a page over a 3rd party API, there is a Runtime Exception if the page renders any view programmatically (e.g. through a node preprocess). The renderPlain() was called from a shutdown function
Failed to start the session because headers have already been sent by "/app/vendor/symfony/http-foundation/Response.php" at line 384.
The site is currently on 9.5.5. I have tested with 9.5.6-dev and it has not been fixed. I have done some debugging and found that the bug was introduced in 9.5.4.
Steps to reproduce
- Use Drupal core 9.5.4+
- Create a node that has a programmatically generated view (i.e. $view = Views::getView()... $view->preview())
- In a shut down function, call renderPlain() on a render array containing the above node
Proposed resolution
I have not got a solution. If I can find a fix I will create a patch and add it here.
If anyone thinks this is working as expected, then please make suggestions on how I can correct this.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | core-render-plain-runtime-exception-3350137-9.patch | 829 bytes | scambler |
| #7 | core-render-plain-runtime-exception-3350137-7.patch | 712 bytes | scambler |
Issue fork drupal-3350137
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
scambler commentedI have found the line which is causing the error and have reverted it to what it was on 9.5.3.
Comment #3
scambler commentedThis next patch is more of a suggestion and not one I will be using on a production site (unless others approve it) as I am not confident of the consequences elsewhere on the site.
Comment #4
cilefen commentedThank you for reporting a bug.
Can we get some sample reproduction code as a way to clarify the steps to reproduce please?
Can you execute a git bisect, or a commit analysis to identify the core commit that changed the behavior please?
Comment #5
cilefen commented^ I fixed a funny auto type in my comment.
Comment #6
scambler commentedComment #7
scambler commented*Correcting file path for patch 2
Comment #8
larowlanFor #4
Comment #9
scambler commented@cilefen I will create some sample code to help reproduce when I next get some free time
Comment #10
lendudeLooking at the proposed change in #9 the culprit would have been #3260652: Feature "Remember the last selection" for views exposed filters doesn't work anymore, and can't recall anything else messing with sessions, so that sounds likely. That did add a check:
But maybe that is not sufficient is some cases?
Sounds pretty edge-case to me, so if that is indeed the only way to run into this, I'm not surprised we missed it.
Comment #11
scambler commentedThe code basically goes like this:
The problem occurs in the renderPlain() when inside the preprocess node and executing/previewing the view. I have tried to tweak this so that it can work on a vanilla site, but I haven't tested it myself yet.
Also worth noting that we have another process that is calls my_module_send_request manually and it works fine. Which further points at the shutdown function being the 'problem'.
Comment #12
scambler commentedComment #14
smustgrave commentedBelieve next steps would be to write a test case or extend an existing test to show this issue.
Comment #16
devad commentedJust to confirm that patch #9 fixed a node render RuntimeExceptions reported by search_api module in my case.
RTBC for me.
My config: D10.3.1. The patch #9 applies successfully.
Previous error message was:
Comment #17
bkosborneI've run into a similar problem, but my node doesn't contain a view, but instead contains a form. FormBuilder::buildForm has this chunk of code
So when it tries to build a form, it checks if the session has batch_form_state data set. If the session isn't yet started, it tries to start it, but NativeSessionStorage refuses to do so because the HTTP response headers have already been set (by the page's initial response).
That code should probably check that the session is actually started before checking if it has data, but I'm not really sure.
Comment #18
dejan0 commentedYes, I’m experiencing the same issue with FormBuilder::buildForm. The $request->getSession()->has('batch_form_state') check always fails when headers have already been sent.
Symfony’s NativeSessionStorage (symfony/http-foundation) explicitly throws an exception in this case: https://github.com/symfony/http-foundation/blob/7.3/Session/Storage/Nati...
It might make sense to add this check in SessionManager::startNow() to handle this case better...
Comment #19
ankondrat4 commentedPatch #18 https://www.drupal.org/project/drupal/issues/3350137#comment-16014932 works for me on Drupal 10.5.0
I have created issue fork with these changes.
Please review.
Comment #21
ankondrat4 commentedComment #22
smustgrave commentedThanks! See it was previously tagged for tests so moving to NW for that.
Comment #24
tvalimaa commentedThe fix
(!headers_sent() && parent::start())is already present in SessionManager.php at line 138 — it's been merged into Drupal core 11.4.5.