Problem/Motivation

If you link to a non-existent route from your code, it breaks the whole webpage on which the link is displayed. A Symfony\Component\Routing\Exception\RouteNotFoundException exception is thrown, and an "unexpected error" page is displayed to users.

This was probably done based on the expectation that it's due to broken code, but it's a pretty harsh solution considering the following scenarios:

  1. If module A links to a route provided by module B, and the two modules are usually used together (but not always), then the developer probably won't notice the problem while writing their code. But as soon as someone uses module A on a site where module B isn't installed, it will break their site. This is not a made-up scenario; it actually happened in core: #2473105: Update hook_help texts that link to modules that can be uninstalled
  2. If module A links to a route provided by module B, and module B's code is updated to no longer define that route, then when a site updates module B to the newest version, module A will break.

Proposed resolution

I would expect this to behave like links in Drupal 7 and earlier do. If code tries to link to a page that doesn't exist, the link itself should render fine. When the user clicks on the link, they should get a 404.

Exactly how to achieve that is questionable though, since in the case of routes it is not clear what URL would display in the browser (the system doesn't have an actual URL to use)...

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

webchick’s picture

Priority: Normal » Major

This feels pretty major to me.

cilefen’s picture

On catching RouteNotFoundException in the proper place(s), perhaps produce a '#' and carry on.

dawehner’s picture

I would expect this to behave like links in Drupal 7 and earlier do. If code tries to link to a page that doesn't exist, the link itself should render fine. When the user clicks on the link, they should get a 404.

I agree we should not blow up, but on the other hand it would be interesting to know for module developers that such a case exist, as they should better introduce a module_exists() or similar. Does logging makes sense in that case?

One major difference to D6/D7 is that we no longer have the path, so as @cilefen wrote we can maybe like to <none>.

opratr’s picture

xjm’s picture

I can see the case for handling a missing route more gracefully, though technically a missing route is broken code.

I don't think linking to # is the correct way to handle this. In addition to the point @dawehner makes about logging, the magic behavior / silent non-failure seems incorrect. If the code links to a resource that doesn't exist, it seems to me like that link should be to a 404. This 404 request could log the fact that the route was missing and/or present this information on the 404 page.

Furthermore, something that appears to be a link but basically does nothing seems like a usability and accessibility problem.

xjm’s picture

Issue tags: -Novice

Not novice at this point since there is not a consensus. :)

cilefen’s picture

I like #5. +1 for a 404 - that is how the web is supposed to work.

cilefen’s picture

To accomplish #5, we would have to create some kind of default 404 route in the system, and modify UrlGenerator::getRoute() to return it.

David_Rothstein’s picture

Well that brings it back to my question in the issue summary:

I would expect this to behave like links in Drupal 7 and earlier do. If code tries to link to a page that doesn't exist, the link itself should render fine. When the user clicks on the link, they should get a 404.

Exactly how to achieve that is questionable though, since in the case of routes it is not clear what URL would display in the browser (the system doesn't have an actual URL to use)...

What do we link to in order to generate a 404? I mean there are lots of possibilities - /fddsafaffdfdasfadsfdas would probably work 99.9999% of the time :) But since we don't know what the code is trying to link to, it's just not obvious what URL the user should see in their browser in this case.

Anyone have any ideas? Do we (or should we) have some kind of dedicated 404-only route for that purpose?

David_Rothstein’s picture

Cross-post :)

cilefen’s picture

Could we not pass the string name of the bogus route as a parameter?

cilefen’s picture

In other words, the URL would be /some/predetermined/404/path/bad_route_name.

tim.plunkett’s picture

Crell’s picture

There's more going on there, but #2532490: Unrouted URLs break toolbar but are hidden by caching is a similar problem space. (Different exception, similar underlying cause: Insufficient of handling of invalid links.) Cross-linking.

dawehner’s picture

Yeah the question is whether we want to switch from exceptions to errors internally and otherwise return safe values like an empty string

Fabianx’s picture

I just ran into the same problem with permissions not realizing that 'access content' is now only provided when 'node' module is installed, which is not in 'testing' profile.

Therefore, I think that problem is pre-existing (In other spaces), we just have to realize that D8 is a lot more strict about links and urls.

Maybe a good idea would be to namespace (further new routes) as a convention with the module name?

Then it is pretty clear it is not a generic route ...

Just some thoughts ...

--

Question: I assume user input urls and links are not affected as they go via a different code path, right?

tim.plunkett’s picture

We shipped with this, and I haven't heard anyone complain since.
I personally think we should mark it works as designed. If you don't own the route name you're linking to, your code should do whatever is necessary to not break if that route goes away (moduleExists, try/catch, ...)

Crell’s picture

It should at least fail gracefully. Even if that's a better description in a global exception catch so you get a useful error message.

dawehner’s picture

This is what is happening:

The website encountered an unexpected error. Please try again later.

Symfony\Component\Routing\Exception\RouteNotFoundException: Route "foo" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 191 of core/lib/Drupal/Core/Routing/RouteProvider.php).
Drupal\Core\Routing\UrlGenerator->getRoute('foo')

I'm not sure whether this is gracefully or not.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

Priority: Major » Normal
Status: Active » Closed (works as designed)

Graceful enough for me

David_Rothstein’s picture

Version: 8.2.x-dev » 8.5.x-dev
Status: Closed (works as designed) » Active

I don't think that's graceful - fixing that was actually the entire point of this issue.

A broken link should really just be a broken link; it should not result in the entire page that the link was on being replaced with an error message and thereby preventing the user from seeing the rest of the page's content.

I noticed recently that if you are on a 404 page and call Url::fromRoute('<current>'), the generated URL points to something like "system/404". It is not obviously the correct behavior in that case, but I think it is correct behavior in the case of this issue - basically it is the answer to the question I asked above about whether there should be a dedicated 404-only route for this purpose.

So I propose that this issue be fixed by making Url::fromRoute('some-path-that-does-not-exist') point to the system/404 page. This could be accompanied by a trigger_error() or similar, so that developers can be informed that there is a broken link in the code.

tim.plunkett’s picture

Title: Linking to a non-existent route breaks the entire webpage on which the link is displayed » Linking to a non-existent route should not throw an exception
Category: Bug report » Feature request
MaskOta’s picture

Status: Active » Needs review
FileSize
1.36 KB

Like so?

dawehner’s picture

I'm wondering whether we could do this kind of error handling maybe further out in the layers ... Errors are something you an't catch in PHP 5.5/PHP 5.6. Maybe the URL generator is a better place?

Status: Needs review » Needs work

The last submitted patch, 25: 2498675-404_for_missing_routes.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.