Custom breadcrumbs from a breadcrumb builder disappear until I clear Varnish cache Unrelated to this question, which is about how to properly inject breadcrumbs as a renderable item into a node template.. I have a custom breadcrumb builder for certain node types with the same sort of cache clearing issue.

After I deploy code to production, and clear the Drupal cache, about 50% of the time anonymous users stop seeing breadcrumbs on these 3 node types. I find that I have to clear cache again, then clear the Varnish cache on the server, and then they appear again for anonymous users. Am I missing something in the build method?

&lt;?php

declare(strict_types = 1);

namespace Drupal\harlib_breadcrumbs;

use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Link;
use Drupal\Core\Routing\AdminContext;
use Drupal\node\NodeInterface;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Url;

/**
 * Class ContentTypeBasedBreadcrumbBuilder.
 *
 * @package Drupal\harlib_breadcrumbs
 */
class ContentTypeBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {

  use StringTranslationTrait;

  /**
   * The AdminContext service.
   *
   * @var \Drupal\Core\Routing\AdminContext
   */
  protected $adminContext;

  /**
   * The AliasManager service.
   *
   * @var \Drupal\Core\Path\AliasManagerInterface
   */
  protected $aliasManager;

  /**
   * The EntityTypeManager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(
    AdminContext $admin_context,
    AliasManagerInterface $alias_manager,
    EntityTypeManagerInterface $entity_type_manager
  ) {
    $this-&gt;adminContext = $admin_context;
    $this-&gt;aliasManager = $alias_manager;
    $this-&gt;entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) : bool {
    if (!$this-&gt;adminContext-&gt;isAdminRoute()) {
      $parameters = $route_match-&gt;getParameters()-&gt;all();

      if (isset($parameters['node']) &amp;&amp; $parameters['node'] instanceof NodeInterface) {
        $types = ['story', 'press_release', 'news_mention'];
        $type = $parameters['node']-&gt;getType();
        return in_array($type, $types);
      }
    }

    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function build(RouteMatchInterface $route_match) : Breadcrumb {
    $node = $route_match-&gt;getParameter('node');
    $breadcrumb = new Breadcrumb();
    $breadcrumb-&gt;addLink(Link::createFromRoute($this-&gt;t('Home'), '&lt;front&gt;'));

    if ($url = $this-&gt;getNodeUrlByAlias('/about')) {
      $breadcrumb-&gt;addLink(Link::fromTextAndUrl($this-&gt;t('About'), $url));
    }

    if ($url = $this-&gt;getNodeUrlByAlias('/about/news')) {
      $breadcrumb-&gt;addLink(Link::fromTextAndUrl($this-&gt;t('News'), $url));
    }

    $breadcrumb-&gt;addCacheContexts(['url.path']);
    $breadcrumb-&gt;addCacheTags(["node:{$node-&gt;nid-&gt;value}"]);  
    return $breadcrumb;
  }

  /**
   * Create a Url object if the alias matches a node object path.
   *
   * @param string $alias
   *   The node alias.
   *
   * @return bool|\Drupal\Core\Url
   *   A Url object, or FALSE if no result found.
   */
  protected function getNodeUrlByAlias(string $alias) {
    $path = $this-&gt;aliasManager-&gt;getPathByAlias($alias);

    if (preg_match('/node\/(\d+)/', $path, $matches)) {
      $id = $matches[1];
      return Url::fromRoute('entity.node.canonical', ['node' =&gt; $id]);
    }

    return FALSE;
  }

}
Pretty sure I found the root cause. I found some errant code here in the else condition.

/**
 * Implements hook_preprocess_block().
 */
function mytheme_preprocess_block(&amp;$variables) {
  if ($variables['plugin_id'] == 'system_breadcrumb_block') {
    if (...) {
      $variables['attributes']['class'][] = 'hl__breadcrumb--light';
    }
    else {
      if (!empty($variables['content']['#links'])) {
        if (count($variables['content']['#links']) == 1) {
          $variables['content'] = [];
          $variables['elements']['content'] = $variables['content'];
        }
      }
    }
  }
}


Deleting the entire else block seems to have corrected the issue, unable to replicate it now.