Custom tokens breaking on node save with "node entity cannot have a URI" error I'm getting a crash on node save when my node contains a custom token.  I'm using the Token Filter module to display tokens in node content.

To make the tokens I followed this tutorial, which is similar to the Token documentation.

Now, with Drupal 8.8, when I save a new node that contains one of my custom tokens, I get a WSOD with this error:


  Drupal\Core\Entity\EntityStorageException: The "node" entity cannot
  have a URI as it does not have an ID in
  Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (line 846 of
  core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).


But the real error appears to be from token:

Drupal\Core\Utility\Token-&gt;generate('ebook', Array, Array, Array, Object) (Line: 196)
Drupal\Core\Utility\Token-&gt;replace('
Test [ebook:custom-type] 2

', Array, Array, Object) (Line: 130)
Drupal\token_filter\Plugin\Filter\TokenFilter-&gt;process('
Test [ebook:custom-type] 2

', 'ja') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 100)
Drupal\Core\Render\Renderer-&gt;doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725', 'silenced_deprecation', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 781)
Drupal\Core\Render\Renderer-&gt;doCallback('#pre_render', Array, Array) (Line: 372)


Weirdly, if I remove the token from the body field when I save the node, the node saves properly-- and then I can edit the node and add the token back in without any error. The error only occurs when the node is saved and it has a token in it.

Drupal tokens like [site:name] work fine; it's just my custom token that causes an error.

Here's the relevant code:

/**
 * Implements hook_token_info().
 */
function MYMODULE_token_info() {

  $ebook_type = [
    'name' =&gt; t('Custom Tokens'),
    'description' =&gt; t('Tokens to make upgrading to D9 harder.'),
  ];

  $ebook['custom-type'] = [
    'name' =&gt; t("Custom type"),
    'description' =&gt; t('Apples or oranges'),
  ];

  return [
    'types' =&gt; ['ebook' =&gt; $ebook_type],
    'tokens' =&gt; ['ebook' =&gt; $ebook],
  ];

}

/**
 * Implements hook_tokens().
 */
function MYMODULE_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {

  $replacements = [];

  if ($type == 'ebook' &amp;&amp; !empty($data['node'])) {
    foreach ($tokens as $name =&gt; $original) {
      switch ($name) {
        case 'custom-type':
          $replacements[$original] = MYMODULE_token_set_custom_type($data['node']);
          break;
      }
    }
  return $replacements;
}

function MYMODULE_token_set_custom_type($node) {
  $alias = $node-&gt;toURL()-&gt;toString();
  preg_match('(/.*/)', $alias, $matches);
  if ($matches[0] == '/apples/') {
    return "Apples";
  }
  else {
    return "Oranges";
  }
}


How do I resolve this?
If the token is being generated before the node has been saved, this will cause a WSOD, since no URI can be created for the node. Entity URIs require an entity ID to built the path to the entity.

As such, I'd change your code to:

function MYMODULE_token_set_custom_type($node) {
  if ($alias = $node-&gt;toURL()-&gt;toString()) {
    preg_match('(/.*/)', $alias, $matches);
    if ($matches[0] == '/apples/') {
      return "Apples";
    }
    else {
      return "Oranges";
    }
  }
}