Tabs: invalidating cache tags does not lead to a rebuild of the tabs I have a custom route defined with a custom access check. Apart from some more checks one of the rules for accessing the path is that a specific custom entity exists. The access check does the following:
  public function access(AccountInterface $account) {
    if ($entity_does_not_exist) {
      return AccessResult::forbidden()-&gt;addCacheTags(['my_unique_cache_tag']);
    }
  }

In my custom entity class I defined the postSave and postDelete methods to invalidate this tag every time an entity is created or deleted:
  public static function postDelete(EntityStorageInterface $storage, array $entities) {
    parent::postDelete($storage, $entities);
    foreach ($entities as $entity) {
      \Drupal::cache('render')-&gt;invalidate('my_unique_cache_tag');
    }
  }

  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    parent::postSave($storage, $update);

    if (!$update) {
      \Drupal::cache('render')-&gt;invalidate(ParliamentPeriodCCController::getCacheTag('my_unique_cache_tag');
    }
  }

My problem is that my custom route is used as a secondary tab in my_module.links.task.yml. What I found out so far is that the tabs are rendered in a block which really gets my unique cache tag (as I see in cache_render table - this is why I defined  \Drupal::cache('render') above - \Drupal::cache() did not work neither) but it seems it never gets invalidated. When I enter a page where the secondary tab was not created because no entity exists it is still not shown when I create the entity afterwards. But when I delete the entry for the block from cache_render it is rendered correctly.
Two suggestions to improve caching:

Even if you don't want to make a decision to allow or deny access you can return a neutral result to add cache metadata.

The custom cache tag can be replaced by the list tag ENTITY_TYPE_list, which is invalidated automatically when entities are added or deleted.


The resulting code:
  public function access(AccountInterface $account) {
    if ($entity_does_not_exist) {
      return AccessResult::forbidden()-&gt;addCacheTags(['my_entity_list']);
    }
    return AccessResult::neutral()-&gt;addCacheTags(['my_entity_list']);
  }

postDelete() and postSave() are no longer necessary.