Problem/Motivation
I've spent too many hours debugging that LogicException and finding the root cause now, so I thought it's time to open this issue.
Finding the root cause for that is very hard and frustrating, and there really isn't a good reason to die hard, since all the information is there that we need, it's just not done 100% properly.
To give some ideas what I had to do:
* In Monitoring in a rest service, I'm calling hook_requirements() and because system_requirements() creates a URL for the cron link, I have to add 10+ lines of code to execute it in my own render execution wrapper. I've also helped 2+ people in IRC that did run into similar problems
* In a payment response processing, I'm saving a node, that node is indexed by search API, and then the solr backend generates a URL to figure out the current domain, so it can index for that. I couldn't care less what it does there, but it completely breaks my page and I have no way of fixing that except changing search_api_solr or executing my node save in such a render execution wrapper.
The exception does not give *any* information about what the missing cacheability it is, where it is coming from and how to fix it. You need to debug that yourself, for example by figuring out what max-age/context/tag bubbled up and then break on Cache::merge*() or similar places and then backtracking. Not many developers will figure that out, you need to know lots of internal stuff.
Also, all the required information *is* present, and for render arrays, we happy accept it and move on, only in case of Response objects, we die with an exception.
Workaround
- See change record All rendering must happen in a render context, early rendering's metadata no longer lost
- #19
- #20
- Matt Oliveira @ https://www.lullabot.com/articles/early-rendering-a-lesson-in-debugging-...
-#33
-#47
Proposed resolution
Remove the exception, just add the cacheability metadata, or ignore it with a warning log message or so if that's not possible.
And if the response object is *not* cacheable, then the exception is completely pointless anyway. This is also related to #2626298: REST module must cache only responses to GET requests and would fix that in a more generic way.
Maybe we could introduce some sort of check again later, as an assert or so.
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|
Issue fork drupal-2638686
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
dawehnerSo yeah instead of the exception we could maybe provide some logging or warning, which at least doesn't have any runtime potential breakage.
For some reasons I even had issues generating some URL in tests, which is, pretty crazy, to say the least.
In general we need a general improvement of the DX of the non trivial bits of the rendering layer.
Comment #3
wim leersThe problem described in #2626298: REST module must cache only responses to GET requests, which can be generalized to just points to plain sloppiness in code. It doesn't make sense to do blocking I/O very very deep in function call stacks. E-mails should be generated and sent in a queue/job runner/batch/cron job/something that does it outside of the request/response flow. And if you really want to do that, then you just have to wrap the sending of an e-mail in a render context.
Well, yes, if you're calling hooks, and particularly hooks that are full of legacy code, then you indeed need to execute it in its own render context. Those hook implementations rely on global state. If they had their dependencies injected, state/context isolated, this would not be necessary. So, yes, when you need to call poorly written code from your controller, it's better to execute that in its own render context.
The exception is only thrown in the first place if the response meets this condition:
$response instanceof AttachmentsInterface || $response instanceof CacheableResponseInterface || $response instanceof CacheableDependencyInterface. Which means it's capable of having cacheability metadata. Which means it may be cached. Which means that if it includes the return value ofhook_requirements(), that the response may need to vary by the cache context that thehook_requirements()invocation bubbled. Otherwise you'll get incorrect results.Yes, all of this is annoying. Yes, I wish we wouldn't have this wrapper. But it's there for correctness.
It seems fishy to me that reindexing for search is happening in the same request. It appears that should also be done out-of-band, i.e. through some queue/cron job/…. And if not, then yes, it should be wrapped in a render context.
All of these examples share one theme: they are about doing things other than creating a response, that happen to be triggered due to historical architectural choices.
I totally agree it's annoying. I totally agree it's unfortunate. I totally wish it could've been avoided. But this was necessary for correctness.
Therefore, I very strongly disagree with:
… but I'd be happy to improve the exception message or help the developer in some other way with tracking down the root cause.
Comment #4
wim leersBerdir replied in IRC:
See:
Comment #5
berdirI've read those, but I still don't agree with the conclusion there :)
* You said "The quoted part means that controllers returning Response objects — which did not exist in D7, so there is no actual transition easing there — would be allowed to be equally messy as render arrays.". Just because there are no Response objects in D7 doesn't mean that there is code that doesn't conceptually the same (by simply printing out the response) and has to be ported to D8 by using a response object.
* As a result of that, there is plenty of legacy code being called that then results in Response objects. It's not like that's all fancy new code that you can actually do anything about.
* REST plugins for example have no way of opting out from caching since the one and only class implements CacheableResponse and its usage is hardcoded, no interface.
* As said, at least for POST and other uncacheable responses, this really is pointless because nothing will be cached. Ever. I thought we already discussed and agreed on that part in Barcelona?
* Your feedback re when to send mails and indexing is not very realistic :) We can't change how those mails are sent. For indexing, in many cases yes, but sometimes you need it to be indexed asap. search_api experimented a lot with queues and delayed indexing and AFAIK eventually gave up on those things (for immediate index).
* In that other issue, you just talked about drupal_render(). That's one thing. If it's your own code, then you can just use renderPlain() and then we can't even detect anymore if you're doing it wrong. If it's not your own code then it's already harder. But the real problem and source for pretty much all my examples is actually generating URL's. Even if you can fix the code with those URL's, the workaround is pretty arcane and requires you to trick the system: You claim to handle cacheability metadata yourself but then ignore it.
* Fabianx in #115 there says "Any such advanced controller can easily do" in regards to executing something in a render context. But it really isn't that easy. And the exception doesn't mention it. As I said, It took me a long time find out the root cause for both the monitoring and payment examples and find an appropriate workaround. And if it takes me hours to resolve something, then I really don't want to think about how it will be for less experienced developers.
* I'm also not sure I buy the "When returning a response object, it's the final thing that we don't change/alter anymore". We do that in many places. We ignore redirect responses if a ?destination is given, FinishResponseSubscriber is doing all kinds of cache-related changes on response objects, AnonymousUserResponseSubscriber adds cacheable metadata and more. Why do all those things but not this? As you said yourself: If you return a CacheableResponse then you opt in to the default caching behaviors/logic. Only if you don't, then we don't change your response for cache-related things.
To summarize, I think the current behavior ignores the reality of our current code/situation and what you can and can't fix as a developer. If the exception would be helpful and tell you what to do, then it might be OK. But the current exception message is really useless :)
Which is why I still think we should change it.
Comment #7
dawehnerI totally agree that this is a MAJOR pain point. Not necessarily for core/contrib development but for actual sites and custom code. Its so easy to have leaking of metadata that you don't even see where this is coming from, especially because enabling a module can cause code to start leaking metadata. For example every node_access module causes additional leaking via #2729137: NodeStorage::loadByProperties should avoid adding node access query tags for example.
Something like
this would help everyone to at least get started.
Comment #8
catchI think to start with should trigger_error() here rather than throw an exception. This happens due to interactions between code which might work independently when being developed, but not when combined on a site, with #2729137: NodeStorage::loadByProperties should avoid adding node access query tags being a good example.
Comment #9
damiankloip commentedI also ran into the same problem recently, and yes, rather painful to debug. During a POST request too.
It's totally feasible that someone would just send an email after some action during a request. Not all sites will be implementing queues and such. It is certainly not necessarily indicative of 'sloppiness' in code. Doing stuff like this has not been a problem for years. I don't think anyone would assume this would cause a problem with their response.
I agree with berdir here. Assuming that we will only ever be doing things to do with generating the response is unrealistic.
Could this be mitigated by doing things like e.g. executing mail() calls inside their own render context? Conceptually it's questionable but it seems like that ship has sailed and the DX tradeoff is worth it.
Comment #10
berdirNot questionable at all IMHO: #2663270: MailManager::mail() should run inside its own render context: it sends e-mails, not (cacheable) responses
Forgot to add that as a related issue. Together with #2663638: Avoid checking the render context to detect early rendering for Non-GET requests. and #2626298: REST module must cache only responses to GET requests, we can cover many problematic cases (in fact, the rest user save problem that was the reasion for creating the now fixed REST/GET issue would be fixed by any of them.
It doesn't cover everything yet, #2729137: NodeStorage::loadByProperties should avoid adding node access query tags is an example for that. trigger_error() sounds like an improvement to me as well, but only if we can also actually provide more information. A trigger_error() that is as hard to resolve as the exception isn't really an improvement, it will just fill up logs.
Yes, this!
Comment #11
damiankloip commentedI had a feeling there might already be an issue like that, thanks berdir.
Comment #12
catch#2744715: Generated URLs should only have cache contexts on GET/HEAD.
URL generation had a rocky ride during 8.x to say the least, one aspect of which was its relationship to the render system in general.
There are not that many places we added cacheability metadata implicitly, so one or two more patches like this ought to cut things down a lot.
Comment #15
skyredwangI also ran into this problem. Same situation. I was confused that there was one debugging hint a 'url.site' was added to the cache context. Then, I read #12, and commented out my URI related generation feature, and the problem was gone.
Comment #17
grasmash commentedCan you reference an example snippet where you do the following?
Comment #18
grasmash commentedThis workaround was effective:
Comment #19
ohthehugemanatee commentedHappened to me in a controller where I used the output of Entity::toUrl in building a redirect. I agree with @berdir: the biggest problem here is the unhelpful message! Secondarily, it doesn't make any sense to fatal on a recoverable problem. This should be a warning level log message IMO. +1 for trigger_error .
For anyone running into this again: you are receiving this fatal because you use a method which counts as rendering, without adding its cache metadata to the render context. The common case (that I keep bumping into, at least), is if you're generating a Url string somewhere. Rendering a Url counts as rendering, and has cache metadata that you should preserve. Instead of:
Use:
Or if you're doing something that's uncacheable anyways, just passing TRUE to toString() claims that you're handling the metadata, and will stop the error. So technically
$node->toUrl()->toString(TRUE)->getGeneratedUrl()is enough.Comment #20
mikejw commentedI had an issue from calling normalize on an entity. Current fix I am using is to wrap it ala other patches (posting for #lazyweb):
Comment #22
gogowitsch commentedComment #23
mstills commentedRan into this same issue because we were generating image style URLs as part of a function that was being called to assemble a much larger JSON response. It was extremely time consuming to track down due to the unhelpful error message. Solutions on Stack Overflow that mention the error will not work in general. The only workaround that was viable for our situation without having to do a massive amount of refactoring was already suggested in #20 - using another render context. Unsure of the performance implications of doing this any time we need to generate a URL
A better error message could at least help people determine what needs to be done in a separate render context without having to trace through dozens and dozens of core classes. Is this issue still getting any attention?
Comment #24
mxh commentedDue to the growing trend of decoupled web apps, I guess this issue is increasing in its relevance, as more developers might try to build a "simple" controller delivering some rendered content as Json feeds. I guess there's more documentation needed, both regards that exception mentioned here and how to properly build Json feed controllers (yep, devs might still want to write their own).
Comment #25
mpp commentedHi @Berdir, I assume you still prefer to remove the exception alltogether or would #12 make sense to fix these issues?
Referencing another issue that seems to be related: RedirectDestination->getAsArray()/get() leaks cacheablity metadata.
Comment #26
davidwhthomas commentedThis error is probably the biggest DX pain for me at the moment, quite obtuse error msg and takes hours to debug and fix.
Currently have this problem loading a comment in a REST response, but only if the comment has a parent comment...
Doesn't help adding cache tags for both the comment and the parent comment, I guess something else related to the render, probably url...
Would be nice if this could be fixed!
Comment #28
clemens.tolboomAdded workaround section adding #19 and #20 plus the excellent debug/workaround blog by Matt Oliveira https://www.lullabot.com/articles/early-rendering-a-lesson-in-debugging-...
Comment #29
roderikComment #30
g.jordan commentedHow do you workaround this issue when POSTing to the JSON:API from outside applications? Anytime I POST to a route like /jsonapi/{node}/*, I get this error. The POST is successful, but the error is logged. I'm POSTing and PATCHing 1000s of nodes nightly, so it's unacceptable to wake up to a 3.5 gigabyte log every morning.
Comment #31
roderikg.jordan[]uky.edu - at the moment, you can only fix the issue by fixing whatever code is responsible for indirectly generating the error.
davidwhthomas - do you still get this error? I can match your description about parent comment, to code I see in rdf_comment_storage_load() and which I've probably addressed in #2335487: Remove the multiple comment optimization from rdf.module. I can't get core to throw the exception by itself, so I am guessing you have custom code doing this. If you can create a test (or alternatively upload a stripped-down version of your controller which I could make into a test) to contribute to that issue... it has a better chance of going RTBC.
Comment #32
blainelang commentedI have a custom REST endpoint that's creates a couple content nodes and then returns a very basic response but I am now getting this error. I had this part of the project working a month ago and I would have done a Drupal 8 core update since then so I'm thinking there was a core change that now does not like my REST Response.
I've checked and am not rendering anything in the code and even removed this code in case that was triggering a render internally with no change
What should I be doing to return a basic array response?
Comment #33
blainelang commentedWell I found a solution but definitely went down the rabbit hole on this one. Another useful blog article was https://blog.dcycle.com/blog/2018-01-24/caching-drupal-8-rest-resource/ which was helpful but it also linked to other resources including https://spinningcode.org/2017/05/cached-json-responses-in-drupal-8 which is much closer to what I needed.
In the end this was all I needed
Comment #34
clemens.tolboomSo the summary was not clear enough. There is CR 2513810 All rendering must happen in a render context, early rendering's metadata no longer lost
Comment #35
kfritscheAs we having a similar problem then discussed in the last comments I created now a follow up ticket (#3080634: Catch EarlyRendering Exception for REST-Resources) for REST calls explicitly (extending ResourceBase), as this seems a special pain point there.
With the attached patch there its not need anymore to always wrap everything around executeInRenderContext().
Comment #36
hchonovThis might be a very stupid question, but why instead of throwing an exception we don't retrieve the cacheable metadata from the context and add it to the response before returning it? Wouldn't that let the page and the dynamic page cache to properly cache the response?
According to the change record 2513810 All rendering must happen in a render context, early rendering's metadata no longer lostthis should be possible automatically, but we want that
But it looks like there are cases where it is not possible to prevent early rendering and one cannot really add a wrapper around so many places where this could happen like the one described in #3080634: Catch EarlyRendering Exception for REST-Resources:
Comment #37
garphyFrom #36 :
I also thought of that lately, wondering why it's not the case. I hope it's not a so stupid suggestion so I decided to take that route (for CacheableResponseInterface in my case as it is in JSON:API use case).
I'm hoping it'll solves issues related to the combined use of advanced node permission (i.e. node access records & grants) alongside JSON:API which is currently kinda broken. (see #3077302: JSON:API Logic exception caused by leaked metadata, #3040603: QueryAccessHandlerBase should document a workaround for leaking metadata, #3028976: Enable an entity query's return value to carry cacheability & possibly others)
Here's a patch in case you want to try it out.
Comment #38
garphyInstead of the patch in #37, which was a quick & dirty attempt to find a workaround to be able to "go live" on some of our projects, I came up with an independent contrib which does the same thing without hacking the core.
It's a more elegant way of solving the issue for most of our JSON:API based decoupled project (YMMV) while preserving the conservative behavior of the core.
https://www.drupal.org/project/jsonapi_earlyrendering_workaround
Comment #40
lexsoft00 commentedHi @garphy,
Thank you ever so much for the module workaround. It works for me.
Drupal Version
8.7.8
Comment #42
narendra.rajwar27@garphy, awesome... patch solution works for me in drupal8.9.3... but patch was not applied.
I was facing issue while creating node of a particular content type via JSON:API, for that content type there was some playaround with drupal cache tags.
For other content type JSON:API was working fine while creating node.
Thanks a lot..!!!!
Error was:
More detailed:
"exception": "LogicException: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\\jsonapi\\ResourceResponse. in {{project}}/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php:154\nStack trace:\n#0 {{project}}/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)\n#1 {{project}}/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\\Core\\EventSubscriber\\EarlyRenderingControllerWrapperSubscriber->Drupal\\Core\\EventSubscriber\\{closure}()\n#2 {{project}}/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw(Object(Symfony\\Component\\HttpFoundation\\Request), 1)\n#3 {{project}}/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#4 {{project}}/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#5 {{project}}/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#6 {{project}}/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\\page_cache\\StackMiddleware\\PageCache->pass(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#7 {{project}}/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\page_cache\\StackMiddleware\\PageCache->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#8 {{project}}/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#9 {{project}}/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#10 {{project}}/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#11 {{project}}/index.php(19): Drupal\\Core\\DrupalKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request))\n#12 {main}\n\nNext Symfony\\Component\\HttpKernel\\Exception\\HttpException: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\\jsonapi\\ResourceResponse. in {{project}}/core/modules/jsonapi/src/EventSubscriber/DefaultExceptionSubscriber.php:48\nStack trace:\n#0 [internal function]: Drupal\\jsonapi\\EventSubscriber\\DefaultExceptionSubscriber->onException(Object(Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent), 'kernel.exceptio...', Object(Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher))\n#1 {{project}}/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func(Array, Object(Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent), 'kernel.exceptio...', Object(Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher))\n#2 {{project}}/vendor/symfony/http-kernel/HttpKernel.php(227): Drupal\\Component\\EventDispatcher\\ContainerAwareEventDispatcher->dispatch('kernel.exceptio...', Object(Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent))\n#3 {{project}}/vendor/symfony/http-kernel/HttpKernel.php(79): Symfony\\Component\\HttpKernel\\HttpKernel->handleException(Object(LogicException), Object(Symfony\\Component\\HttpFoundation\\Request), 1)\n#4 {{project}}/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\\Component\\HttpKernel\\HttpKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#5 {{project}}/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\\Core\\StackMiddleware\\Session->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#6 {{project}}/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\\Core\\StackMiddleware\\KernelPreHandle->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#7 {{project}}/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\\page_cache\\StackMiddleware\\PageCache->pass(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#8 {{project}}/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\\page_cache\\StackMiddleware\\PageCache->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#9 {{project}}/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#10 {{project}}/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\\Core\\StackMiddleware\\NegotiationMiddleware->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#11 {{project}}/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\\StackedHttpKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request), 1, true)\n#12 {{project}}/index.php(19): Drupal\\Core\\DrupalKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request))\n#13 {main}"Comment #43
narendra.rajwar27Adding re-rolled patch from comment #37 for drupal 9.1.x
Comment #44
clemens.tolboomComment #45
claudfernandes commented#37 works for me, thanks for that
Comment #47
roderikAs a followup on generating URLs for responses:
I figured this would always be safe. As it turns out, even though the code itself is good, a
toString()call can cause code in other modules to be invoked... which can do unsafe things.Example: have content_moderation enabled, upgrade to Rules 8.x-3.0-alpha6 -> the above starts throwing the "early rendering" exception (because Rules does an
Url::fromRoute(...)->toString()during your call totoString(TRUE), and leaks metadata). #3161036-6: CurrentPathContext inadvertently bubbles up 'route' cache contextThis makes things more tedious: to fully protect against 'outside' code causing the exception, even all
Url::from*()andUrl::toString()calls need to be wrapped inside a render context.For those interested: here's a method which executes a callable (i.e. any potentially-endangered code which you'd execute during a request) plus the URL generation, into a render context and returns a TrustedRedirectResponse: https://git.drupalcode.org/project/samlauth/blob/8.x-3.x/src/Controller/...
Call example:
Comment #48
roderikAlso, half related: just stumbled onto #2528598: Opt out of cacheablity protection on route level. (I wish I'd know of its existence years ago - I didn't know if it made sense to propose this myself.)
Comment #49
mxr576Wow! The documentation in samlauth/blob/8.x-3.x/src/Controller/ExecuteInRenderContextTrait.php is very insightful and well-detailed. It is actual learning material :) Kudos to @roderik
Comment #50
roderikThanks :) All the info in the trait's code comments is from other sources (issue comments from Wim Leers/Berdir/Crell/..., the Lullabot article). But it's been quite a challenge to form an opinion on how different pain points relate, and which to highlight in a concise summary. (4 multi-day sessions between 2017 and now, so far. I respect that experienced core developers have had no time for this and needed to rush things to get Drupal 8 out the door - but... by now I also get that it hasn't been easy for anyone else to make progress on this.)
In case anyone's wondering why this is in a trait hidden away in a random contrib module, and not made into a patch on this issue: in my estimation, this issue is explicitly stalled since January 2016 / comment #4. The last few patches ignore the comments made around there.
(If I could put my own spin on making a short summary: the best single-comment summary for the rationale behind the current implementation is in #2450993-133: Rendered Cache Metadata created during the main controller request gets lost. Unfortunately, a large part of the points summarized (1, 3, 5) there is untrue. I assume that was harder to see in January 2016 but it's clear now - now that the remaining pain points are hidden behavior of Url::toString() calls, Drupal Core implicitly forcing developers to use cacheable responses in at least a subset of redirect responses, and lack of help/documentation pointing these things out.)
If anyone wants to form a mini initiative / task force around getting URL stuff resolved: feel free to contact me. I think this will need the following steps:
My own idea for a minimum scope to work on, is to get clarifying help text into Url::toString() and/or other places in documentation, making people aware of the risks of outputting URLs. (Because we're not doing that, thereby letting people introduce new fatal bugs unwittingly.) Last time I set that goal was February 2019, which devolved into working on #2735575-66: Make Url::toString(TRUE) explicit by having a Url::generate() method, as well as having GeneratedUrl::toString for more than a month and then being distracted by other things. Now, after spending time on building that new trait with fresh thoughts... I'll de-prioritize this again for a while... unless other people want to join forces.
Comment #55
proweb.ua commented#43 works
Comment #56
wombatbuddy commentedThe patch #43 does the job.
Comment #57
kasperbasse91 commented#43 works
Comment #58
bradjones1Is anyone at DrupalCon Europe/Prague (2022) interested in discussing #50?
Comment #60
smustgrave commentedThe last patch seemed to have failures. Which makes me think it will need it's own tests as it's breaking the existing ones.
Comment #62
anisha786 commentedDrupal :9.5.9
Module : Rest Block Layout
Issue when Api call /block-layout?_format=json&path=/test It is giving error Previously on drupal 8.7.5 it is working fine but after version updation it is giving error
Error Message : LogicException: The controller result claims to be providing
relevant cache metadata, but leaked metadata was detected. Please
ensure you are not rendering content too early. Returned object class:
Drupal\rest\ResourceResponse. in
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()
(line 158 of
\core\lib\Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber.php).
I have applied below #43 mentioned patch but from below patch error is gone , but in my website if user view multiple pages in separate tabs it is showing wrong node content data. It shows data which was latest cached.
. Let me know if anyone has solution how to fix it .
Comment #63
leksat commented#43 still works 🎉 The nightmare is over for me 😅
Comment #64
damien laguerre commentedIdem, #43 solved my problem.
I'm experiencing this problem in a REST resource. The context user.node_grants:view throws this error.
Comment #65
solideogloria commentedComment #66
solideogloria commentedPatch #43 still applies to 10.3.x. However, it should probably be converted to a merge request.
Also, the result after patching is this:
The
|| $response instanceof CacheableResponseInterfacecheck should be removed from the second else-if shown.Comment #69
solideogloria commentedThe new merge request targets 11.x and has the changes from patch #43, as well as the change I mentioned in #66.
Comment #70
solideogloria commentedI fixed the broken test. It just needed to be updated, since the code changes result in a success response instead of an HTTP 500.
This means that the changes have test coverage. All tests are passing successfully now.
Comment #71
solideogloria commentedPlease review the changes and check whether the test coverage for early rendering needs to be expanded to handle more cases.
See
EarlyRenderingControllerTest::testEarlyRendering. The changed test case is the one from lines 75-78Comment #76
smustgrave commentedHiding patches and empty branches
Ran test-only feature to confirm coverage
believe the issue summary proposed solution lines up with moving the CacheableResponseInterface up to it's own check.
Believe this is good and has gotten a few comments about it working for others.
Comment #77
dwwI landed here chasing various things. For how long this issue has been open, how many comments, patches and effort went into this, I was amazed / shocked / delighted / heartbroken that the diffstat on the changes tab in the MR is: +7 / - 3. 😂 Thrilled to see it's actually RTBC at this point, and hopefully going to land soon. +1 to the RTBC. The code changes are great. The test change is that we now get to check for better outcomes to a common DX problem. 🚀 it!
Thanks,
-Derek
p.s. Starting to save credit. Could use a closer look before commit.
Comment #78
dwwHate to ask, but do we need a change record for this?
Comment #79
quietone commentedThe title of this does not match what I read in the patch, it is not removing an exception. I went to improve that using information in the proposed resolution. However, that describes some options. So, I am sadly setting back to NW for a descriptive title and a tweak to the proposed resolution so that it what is being changed.
Comment #80
dwwI opened an MR thread to document how the diff is removing an exception. I'm a little sad to change the title at all, but how's this?
Comment #81
solideogloria commentedI think the new title is acceptable.
Comment #82
smustgrave commentedTitle update seems good.
Comment #83
catchI reread this a fair bit.
I think given we have all the information here, and a way to apply it to the response, it's fine to do that and not throw the exception. As discussed in the issue, if we were able to inform developers at development time via an assertion or useful error message that stuff isn't being passed around correctly, that might be better, but there's no solution for this in several years now. The exception hitting on runtime with no obvious way to debug doesn't help anyone.
I wonder how many people will hit the remaining exception here, but I guess we'll find out whether that's an issue or not down the line.
Tried to do my best with issue credit but it's a very long issue so apologies if anyone was overlooked.
Committed/pushed to 11.x and cherry-picked to 10.3.x, thanks!