Following an upgrade to jsonapi release 1.22, I am getting the following error in the log:

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 Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (line 154 of /home/james/vmg/fbportal/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php).

I have used Postman to make a request:
{{protocol}}://{{host}}/jsonapi/node/article?_format=api_json
where {{protocol}} is "http" and {{host}} is "fbportal.helene.lan" (which is my development environment).

The response is:

{
    "errors": [
        {
            "title": "Internal Server Error",
            "status": 500,
            "detail": "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.",
            "links": {
                "info": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1"
            },
            "code": 0
        }
    ]
}

I downgraded the environment to jsonapi release 1.21 and the error message disappeared, with the expected list of articles being returned.

I also removed the custom fields I had added to the article content type, but the error persisted with jsonapi release 1.22.

Comments

jlscott created an issue. See original summary.

jayelless’s picture

Inspecting the code in EarlyrenderingControllerWrapperSubscriber

    // If early rendering happened, i.e. if code in the controller called
    // drupal_render() outside of a render context, then the bubbleable metadata
    // for that is stored in the current render context.
    if (!$context->isEmpty()) {
      /** @var \Drupal\Core\Render\BubbleableMetadata $early_rendering_bubbleable_metadata */
      $early_rendering_bubbleable_metadata = $context->pop();

the value of $early_rendering_bubbleable_metadata contains only

cacheContexts = [
  "user.node_grants:view"
]

Does this mean that the problem lies with node access permissions? My site is using the group module, although articles are not part of any group. I will try this problem on an environment that is not using group.

jordan.jamous’s picture

Same issue here!

jayelless’s picture

I used a separate instance of D8 (8.6.x-dev), with the jsonapi, group, and taxonomy modules installed. The jsonapi response to the query used above was the expected list of articles.

I upgraded my development environment to D8.6.x-dev, but the error still persisits???

jayelless’s picture

I deleted all the groups and group_content entities from the database and then rebuilt the permissions in my development environment, but the leaked metadata error was still reported. However, when I un-installed the group node (gnode) module, the error disappeared and th expected list of articles was returned.

I then re-installed the group node module, and rebuilt permissions, but the error did not come back??? It transpired that doing a drush cache-rebuild was not sufficient. I had to truncate the cache tables in the database to observe the re-appearance of the error message.

Following a series of tests, I can confirm that if the group node module is installed, node access permissions rebuilt, and caches properly cleared, then the leaked metadata message is reported. If the group module is un-installed, node access permissions rebuilt (happens automatically), and caches properly cleared, then the expected list of content is returned. There is no need to have any groups defined, nor to have any group_content in the database.

jayelless’s picture

Title: Release 1.22 is leaking metadata » Group Node is leaking metadata
Project: JSON:API » Group

It appears that this issue should really be on the group issue queue. Moving there.

jayelless’s picture

Version: 8.x-1.x-dev » 8.x-1.0-rc2
Issue summary: View changes
jayelless’s picture

Title: Group Node is leaking metadata » Node access is leaking metadata
Project: Group » JSON:API
Version: 8.x-1.0-rc2 » 8.x-1.x-dev

Further testing shows that the problem is present when the nodeaccess module is installed, so this issue appears to be generic when any node access functionality is enabled. Moving this issue back to project jsonapi.

To check: install and enable nodeaccess, rebuild the access permissions, and truncate the cache tables.

jayelless’s picture

The leaked metadata of "user.node_grants:view" is added to the cacheContexts by the following code in hook_query_node_access_alter() in node.module:

  // Bubble the 'user.node_grants:$op' cache context to the current render
  // context.
  $request = \Drupal::requestStack()->getCurrentRequest();
  $renderer = \Drupal::service('renderer');
  if ($request->isMethodCacheable() && $renderer->hasRenderContext()) {
    $build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]];
    $renderer->render($build);
  }

So why is the metadata not being correctly bubbled?

jayelless’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev

The change from v1.21 to 1.22 that has revealed this error was the replacement of the following code

    $context = new RenderContext();
    $response = $this->renderer
      ->executeInRenderContext($context, function () use ($resource, $action, $parameters, $extra_parameters) {
        return call_user_func_array([$resource, $action], array_merge($parameters, $extra_parameters));
      });
    if (!$context->isEmpty()) {
      $response->addCacheableDependency($context->pop());
    }

    return $response;

by the single line

    return call_user_func_array([$resource, $action], array_merge($parameters, $extra_parameters));

It would appear that the code block above was added previously to address a similar issue as discussed in #2952714: Group module's GroupAccessResult::allowedIfHasGroupPermission(s)() does not include cacheability. However, the issue appears to be more significant than the group module or the nodeaccess module.

I can also confirm that the same issue is present with jsonapi release 2.x-dev. Changing the issue metadata.

david spiessens’s picture

Same issue here. We don't use the group module.

pwolanin’s picture

I seem to be seeing the same. I think it relates to our use of the consumer_image_styles module or maybe filefield_paths module. The problem code has cache context of url.site so likely related to Url generation.

wim leers’s picture

Assigned: Unassigned » wim leers
Issue tags: +D8 cacheability, +API-First Initiative

Thanks for reporting!

On it :)

wim leers’s picture

Title: Node access is leaking metadata » JSON API + hook_node_grants() implementations: accessing /jsonapi/node/article as non-admin user results in a cacheability metadata leak

More accurate title.

(Reproduced the problem.)

wim leers’s picture

wim leers’s picture

Assigned: wim leers » Unassigned
Status: Active » Needs review
Related issues: +#2794385: Node grants make it impossible to return a CacheableResponse from a controller
StatusFileSize
new6.65 KB

#2948666: Remove JSON API's use of $context['cacheable_metadata'] is still a good thing: that was catching bubbled cacheability at a very very broad level.

We just need to bring back a little bit of that, for query execution. And fortunately, there's only a single place in all of JSON API where we do that. 👍

Most of the attached patch is updating boilerplate to get the renderer service injected into the right place.

Thank you for this important bug discovery!

P.S.: hey look at that, a certain @gabesullice fellow filed an issue for the general version of this nearly two years ago: #2794385: Node grants make it impossible to return a CacheableResponse from a controller :)

Status: Needs review » Needs work

The last submitted patch, 17: 2984964-17.patch, failed testing. View results

jayelless’s picture

Thanks for the patch. I can confirm that the patch in #17 has fixed the problem in both of my environments:

  • Drupal 8.5.5 + jsonapi 1.22 + group
  • Drupal 8.6.0-dev + jsonapi 2.x-dev + nodeaccess

I will leave the status on "Needs work" as the patch failed testing.

wim leers’s picture

Assigned: Unassigned » wim leers
Status: Needs work » Needs review
Issue tags: +Needs tests
StatusFileSize
new1.15 KB
new8.78 KB

This should be green.

But this still needs explicit test coverage. On it.

wim leers’s picture

Assigned: wim leers » Unassigned
Issue tags: -Needs tests
StatusFileSize
new1.45 KB
new10.19 KB

The test-only patch is also the interdiff.

wim leers’s picture

StatusFileSize
new633 bytes
new10.32 KB

Might as well explicitly test this.

The last submitted patch, 21: 2984964-21-test_only_FAIL.patch, failed testing. View results

gabesullice’s picture

Status: Needs review » Reviewed & tested by the community

Man, it's so weird that we need @renderer to execute a query...

But, I think this is a small and proper change. RTBC :)

  • Wim Leers committed bdcd14f on 8.x-2.x
    Issue #2984964 by Wim Leers, jlscott, DavidSpiessens, gabesullice: JSON...
wim leers’s picture

Status: Reviewed & tested by the community » Fixed

#24: no disagreement there. The query system sadly was never updated to pass on relevant cacheability in a sane way… this was the unfortunate work-around so we could ship D8.

Credited everybody who helped shape this patch! Thanks! 🙏

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

steven.wichers’s picture

This fixes the issue on the 1.x branch as well.

gabesullice’s picture

Version: 8.x-2.x-dev » 8.x-1.x-dev
Priority: Normal » Critical
Status: Closed (fixed) » Active

I think this issue deserves to be backported to 1.x in the #2990908: [1.x] Drupal core compatibility release

wim leers’s picture

Status: Active » Fixed

Let's reopen this when we actually work on it; otherwise it's gonna be confusing. (In core we have the principle of opening a separate issue for a backport to avoid this same confusion, but I think that's disproportionately onerous in the case of JSON API.)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

rpayanm’s picture

Project: JSON:API » Drupal core
Version: 8.x-1.x-dev » 8.9.x-dev
Component: Code » jsonapi.module

Moving to Drupal core's issue queue.

I'm working on https://www.drupal.org/project/drupal/issues/3122113