We're using our website as a decoupled Drupal, and the front-end needs various Metatag information including the canonical URL.
As the default token for the canonical url in the Metatag module is [node:url], the front-end receives the absolute path to the Node while - for obvious reasons - they prefer to have the relative path to the Node.

However, when I change the [node:url] token to [node:url:relative], this fatal error is thrown on creating the Node:

Drupal\Core\Entity\EntityMalformedException: The "node" entity cannot have a URI as it does not have an ID in Drupal\Core\Entity\EntityBase->toUrl() (line 192 of core/lib/Drupal/Core/Entity/EntityBase.php).

This seems to be triggered by this block of code:

    if (($url_tokens = \Drupal::token()->findWithPrefix($tokens, 'url'))) {
      $replacements += \Drupal::token()->generate('url', $url_tokens, ['url' => $node->toUrl()], $options, $bubbleable_metadata);
    }

which calls $node->toUrl() where the error is triggered due to $node not yet having an ID.

Issue fork token-3216016

Command icon 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

vodde83 created an issue. See original summary.

berdir’s picture

I suppose we can put a try/catch around that, but why would you call this on a node that wasn't saved yet? I don't see node_tokens() catching that error, so I would assume that should give you the same error.

vodde83’s picture

It only seems to be triggered with a 'chained' token on [node:url] ( e.g. [node:url:relative], [node:url:absolute], .. ) due to the IF statement.
If I don't chain any further, all goes well. I'm guessing Core doesn't deal with chained token on [node:url].

And why? Because I want the relative URL to appear on the canonical URL meta-tag :)
If I initially create the node with [node:url], and afterwards update the canonical URL with [node:url:relative] and save again, there's no error.
But that is quite cumbersome for the customer to do.

I could apply some magic myself during initial Node creation and updating, but I would assume that chained [node:url] -tokens should work out-of-the-box.

berdir’s picture

I'm not asking why you want that token, I'm asking why you're doing token replacements before the node is saved. An unsaved node has no URL, relative or otherwise.

berdir’s picture

And I just double-checked that node:url causes the exact same exception.

$node = \Drupal\node\Entity\Node::create(['type' => 'article']);
\Drupal::token()->replace('[node:url]', ['node' => $node]);

Drupal\Core\Entity\EntityMalformedException with message 'The "node" entity cannot have a URI as it does not have an ID'
berdir’s picture

Status: Active » Postponed (maintainer needs more info)
staceroni’s picture

I am seeing the same message for [node:edit-url] when trying to use something in configuring the default Layout (layout builder) for a node.
In this context, it is a layout configuration, the node doesn't have an ID. I get a stack trace:

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Entity\EntityMalformedException</em>: The &quot;node&quot; entity cannot have a URI as it does not have an ID in <em class="placeholder">Drupal\Core\Entity\EntityBase-&gt;toUrl()</em> (line <em class="placeholder">192</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/EntityBase.php</em>). <pre class="backtrace">node_tokens(&#039;node&#039;, Array, Array, Array, Object)
call_user_func_array(&#039;node_tokens&#039;, Array) (Line: 403)
Drupal\Core\Extension\ModuleHandler-&gt;invokeAll(&#039;tokens&#039;, Array) (Line: 304)
Drupal\Core\Utility\Token-&gt;generate(&#039;node&#039;, Array, Array, Array, Object) (Line: 196)
Drupal\Core\Utility\Token-&gt;replace(&#039;[node:edit-url]&#039;, Array) (Line: 729)

This is a different scenario as described above, however I hope that it can be resolved by not providing these tokens for a node that doesn't yet have an ID.

staceroni’s picture

stanleyfernandes’s picture

consider this issue also to fix the error reported :
https://www.drupal.org/project/yoast_seo/issues/3110455

gcb’s picture

StatusFileSize
new842 bytes

@Berdir the core module layout_builder regularly renders content with sample entities in its UX. These sample entities do not have IDs. As a result, if you use a URI token anywhere on a default layout, the layout builder screen is crashed by this issue.

The function \Drupal\Core\Entity\EntityBase::toUrl() begins with a check for a Null entity ID, and throws an exception if there's no ID. Passing an entity to this function without checking for an ID first is asking for whitescreens -- new, unsaved entities are valid for rendering according to Drupal standards. See the discussion in https://www.drupal.org/project/drupal/issues/3069001 about having contrib modules sensibly handling "preview" content.

gcb’s picture

Status: Postponed (maintainer needs more info) » Needs review
tim.plunkett’s picture

+++ b/token.tokens.inc
@@ -569,7 +569,7 @@ function token_tokens($type, array $tokens, array $data, array $options, Bubblea
+    if (($url_tokens = \Drupal::token()->findWithPrefix($tokens, 'url')) && $node->id() !== NULL) {

Could also use empty($node->in_preview). That pre-dates LB (think original node preview) but is also set when LB generates the sample entity.

Prashant.c made their first commit to this issue’s fork.

berdir’s picture

Version: 8.x-1.9 » 8.x-1.x-dev
Status: Needs review » Needs work

I don't think in preview is the right thing to check here. you can also preview an existing, already saved node with an ID.

We have an existing case that checks for isNew(), for the generic entity:url token, no strong preference betweenn isNew() and id(). I suppose id() could in edge cases be more reliable.

Remaining tasks:
* As mentioned earlier, node_tokens() in core has the same issue, aka the :url and :edit-url tokens, and a core issue should be opened to improve this.
* Either add checks for other toUrl() calls in token replacements in this module or create a follow-up.
* As usual, tests will make it more likely that this gets committed. Look for exsting tests for those tokens, copy paste with a new node, ensure it doesn't throw an error (assert for not replacing the token).

joelpittet’s picture

Status: Needs work » Reviewed & tested by the community

We used MR 52 to explicitly fix this error in preview (usually I don't enable preview on nodes...)

joelpittet’s picture

Rerolled it into the new class for the hooks.

joelpittet’s picture

@berdir, will that suffice for the test coverage?

I tried to avoid parsing the URL but came up short and the core tests for Preview didn't help fill in the gaps enough.

It is red/green on the issue though.

josephr5000’s picture

StatusFileSize
new2.74 KB

Freezing current mr52 state as a patch to give a convenient reference for our composer.json

josephr5000’s picture

StatusFileSize
new2.74 KB

Oops, wrong file extension. Here's a correction.

  • berdir committed 90ae544f on 8.x-1.x authored by prashant.c
    fix: #3216016 Error: "The 'node' entity cannot have a URI as it does not...
berdir’s picture

Status: Reviewed & tested by the community » Fixed

I would have just created a new node in code and a kernel test, this seems a bit fragile if core ever changes how previews work, but I guess we'll deal with it then.

Merged.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

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